diff --git a/spring-core/src/main/java/org/apache/commons/logging/LogFactory.java b/spring-core/src/main/java/org/apache/commons/logging/LogFactory.java index 3c82fdddb3..046b6983bf 100644 --- a/spring-core/src/main/java/org/apache/commons/logging/LogFactory.java +++ b/spring-core/src/main/java/org/apache/commons/logging/LogFactory.java @@ -17,6 +17,7 @@ package org.apache.commons.logging; import java.io.Serializable; +import java.util.logging.LogRecord; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; @@ -28,13 +29,14 @@ import org.slf4j.spi.LocationAwareLogger; /** * A minimal incarnation of Apache Commons Logging's {@code LogFactory} API, - * providing just the two common static {@link Log} lookup methods. - * This should be source and binary compatible with all common use of the - * Commons Logging API (i.e. {@code LogFactory.getLog(Class/String)} setup). + * providing just the common {@link Log} lookup methods. This is inspired + * by the JCL-over-SLF4J bridge and should be source as well as binary + * compatible with all common use of the Commons Logging API (in particular: + * with {@code LogFactory.getLog(Class/String)} field initializers). * - *
This implementation does not support any of Commons Logging's flexible - * configuration. It rather only checks for the presence of the Log4J 2.x API - * and the SLF4J 1.7 API in the framework classpath, falling back to + *
This implementation does not support Commons Logging's original provider + * detection. It rather only checks for the presence of the Log4j 2.x API + * and the SLF4J 1.7 API in the Spring Framework classpath, falling back to * {@code java.util.logging} if none of the two is available. In that sense, * it works as a replacement for the Log4j 2 Commons Logging bridge as well as * the JCL-over-SLF4J bridge, both of which become irrelevant for Spring-based @@ -42,11 +44,18 @@ import org.slf4j.spi.LocationAwareLogger; * Commons Logging API jar anymore either). Furthermore, for simple setups * without an external logging provider, Spring does not require any extra jar * on the classpath anymore since this embedded log factory automatically - * switches to {@code java.util.logging} in such a scenario. + * delegates to {@code java.util.logging} in such a scenario. * *
Note that this Commons Logging variant is only meant to be used for - * framework logging purposes, both in the core framework and in extensions. - * For applications, prefer direct use of Log4J or SLF4J or {@code java.util.logging}. + * infrastructure logging purposes in the core framework and in extensions. + * It also serves as a common bridge for third-party libraries using the + * Commons Logging API, e.g. Apache HttpClient, Castor and HtmlUnit, bringing + * them into the same consistent arrangement without any extra bridge jars. + * + *
For logging need in application code, prefer direct use of Log4j 2.x
+ * or SLF4J or {@code java.util.logging}. Simply put Log4j 2.x or Logback
+ * (or another SLF4J provider) onto your classpath, without any extra bridges,
+ * and let the framework auto-adapt to your choice.
*
* @author Juergen Hoeller
* @since 5.0
@@ -58,11 +67,11 @@ public abstract class LogFactory {
static {
ClassLoader cl = LogFactory.class.getClassLoader();
try {
- // Try Log4J 2.x API
+ // Try Log4j 2.x API
cl.loadClass("org.apache.logging.log4j.spi.ExtendedLogger");
logApi = LogApi.LOG4J;
}
- catch (ClassNotFoundException ex) {
+ catch (ClassNotFoundException ex1) {
try {
// Try SLF4J 1.7 SPI
cl.loadClass("org.slf4j.spi.LocationAwareLogger");
@@ -103,7 +112,13 @@ public abstract class LogFactory {
case SLF4J:
return Slf4jDelegate.createLog(name);
default:
- return new JavaUtilLog(name);
+ // Defensively use lazy-initializing delegate class here as well since the
+ // java.logging module is not present by default on JDK 9. We are requiring
+ // its presence if neither Log4j nor SLF4J is available; however, in the
+ // case of Log4j or SLF4J, we are trying to prevent early initialization
+ // of the JavaUtilLog adapter - e.g. by a JVM in debug mode - when eagerly
+ // trying to parse the bytecode for all the cases of this switch clause.
+ return JavaUtilDelegate.createLog(name);
}
}
@@ -167,6 +182,14 @@ public abstract class LogFactory {
}
+ private static class JavaUtilDelegate {
+
+ public static Log createLog(String name) {
+ return new JavaUtilLog(name);
+ }
+ }
+
+
@SuppressWarnings("serial")
private static class Log4jLog implements Log, Serializable {
@@ -182,8 +205,8 @@ public abstract class LogFactory {
}
@Override
- public boolean isDebugEnabled() {
- return logger.isEnabled(Level.DEBUG, null, null);
+ public boolean isFatalEnabled() {
+ return logger.isEnabled(Level.FATAL, null, null);
}
@Override
@@ -192,8 +215,8 @@ public abstract class LogFactory {
}
@Override
- public boolean isFatalEnabled() {
- return logger.isEnabled(Level.FATAL, null, null);
+ public boolean isWarnEnabled() {
+ return logger.isEnabled(Level.WARN, null, null);
}
@Override
@@ -201,36 +224,16 @@ public abstract class LogFactory {
return logger.isEnabled(Level.INFO, null, null);
}
+ @Override
+ public boolean isDebugEnabled() {
+ return logger.isEnabled(Level.DEBUG, null, null);
+ }
+
@Override
public boolean isTraceEnabled() {
return logger.isEnabled(Level.TRACE, null, null);
}
- @Override
- public boolean isWarnEnabled() {
- return logger.isEnabled(Level.WARN, null, null);
- }
-
- @Override
- public void debug(Object message) {
- logger.logIfEnabled(FQCN, Level.DEBUG, null, message, null);
- }
-
- @Override
- public void debug(Object message, Throwable exception) {
- logger.logIfEnabled(FQCN, Level.DEBUG, null, message, exception);
- }
-
- @Override
- public void error(Object message) {
- logger.logIfEnabled(FQCN, Level.ERROR, null, message, null);
- }
-
- @Override
- public void error(Object message, Throwable exception) {
- logger.logIfEnabled(FQCN, Level.ERROR, null, message, exception);
- }
-
@Override
public void fatal(Object message) {
logger.logIfEnabled(FQCN, Level.FATAL, null, message, null);
@@ -242,23 +245,13 @@ public abstract class LogFactory {
}
@Override
- public void info(Object message) {
- logger.logIfEnabled(FQCN, Level.INFO, null, message, null);
+ public void error(Object message) {
+ logger.logIfEnabled(FQCN, Level.ERROR, null, message, null);
}
@Override
- public void info(Object message, Throwable exception) {
- logger.logIfEnabled(FQCN, Level.INFO, null, message, exception);
- }
-
- @Override
- public void trace(Object message) {
- logger.logIfEnabled(FQCN, Level.TRACE, null, message, null);
- }
-
- @Override
- public void trace(Object message, Throwable exception) {
- logger.logIfEnabled(FQCN, Level.TRACE, null, message, exception);
+ public void error(Object message, Throwable exception) {
+ logger.logIfEnabled(FQCN, Level.ERROR, null, message, exception);
}
@Override
@@ -270,6 +263,36 @@ public abstract class LogFactory {
public void warn(Object message, Throwable exception) {
logger.logIfEnabled(FQCN, Level.WARN, null, message, exception);
}
+
+ @Override
+ public void info(Object message) {
+ logger.logIfEnabled(FQCN, Level.INFO, null, message, null);
+ }
+
+ @Override
+ public void info(Object message, Throwable exception) {
+ logger.logIfEnabled(FQCN, Level.INFO, null, message, exception);
+ }
+
+ @Override
+ public void debug(Object message) {
+ logger.logIfEnabled(FQCN, Level.DEBUG, null, message, null);
+ }
+
+ @Override
+ public void debug(Object message, Throwable exception) {
+ logger.logIfEnabled(FQCN, Level.DEBUG, null, message, exception);
+ }
+
+ @Override
+ public void trace(Object message) {
+ logger.logIfEnabled(FQCN, Level.TRACE, null, message, null);
+ }
+
+ @Override
+ public void trace(Object message, Throwable exception) {
+ logger.logIfEnabled(FQCN, Level.TRACE, null, message, exception);
+ }
}
@@ -285,40 +308,36 @@ public abstract class LogFactory {
this.logger = logger;
}
- public boolean isDebugEnabled() {
- return this.logger.isDebugEnabled();
+ public boolean isFatalEnabled() {
+ return isErrorEnabled();
}
public boolean isErrorEnabled() {
return this.logger.isErrorEnabled();
}
- public boolean isFatalEnabled() {
- return this.logger.isErrorEnabled();
+ public boolean isWarnEnabled() {
+ return this.logger.isWarnEnabled();
}
public boolean isInfoEnabled() {
return this.logger.isInfoEnabled();
}
+ public boolean isDebugEnabled() {
+ return this.logger.isDebugEnabled();
+ }
+
public boolean isTraceEnabled() {
return this.logger.isTraceEnabled();
}
- public boolean isWarnEnabled() {
- return this.logger.isWarnEnabled();
+ public void fatal(Object message) {
+ error(message);
}
- public void debug(Object message) {
- if (message instanceof String || this.logger.isDebugEnabled()) {
- this.logger.debug(String.valueOf(message));
- }
- }
-
- public void debug(Object message, Throwable exception) {
- if (message instanceof String || this.logger.isDebugEnabled()) {
- this.logger.debug(String.valueOf(message), exception);
- }
+ public void fatal(Object message, Throwable exception) {
+ error(message, exception);
}
public void error(Object message) {
@@ -333,12 +352,16 @@ public abstract class LogFactory {
}
}
- public void fatal(Object message) {
- error(message);
+ public void warn(Object message) {
+ if (message instanceof String || this.logger.isWarnEnabled()) {
+ this.logger.warn(String.valueOf(message));
+ }
}
- public void fatal(Object message, Throwable exception) {
- error(message, exception);
+ public void warn(Object message, Throwable exception) {
+ if (message instanceof String || this.logger.isWarnEnabled()) {
+ this.logger.warn(String.valueOf(message), exception);
+ }
}
public void info(Object message) {
@@ -353,6 +376,18 @@ public abstract class LogFactory {
}
}
+ public void debug(Object message) {
+ if (message instanceof String || this.logger.isDebugEnabled()) {
+ this.logger.debug(String.valueOf(message));
+ }
+ }
+
+ public void debug(Object message, Throwable exception) {
+ if (message instanceof String || this.logger.isDebugEnabled()) {
+ this.logger.debug(String.valueOf(message), exception);
+ }
+ }
+
public void trace(Object message) {
if (message instanceof String || this.logger.isTraceEnabled()) {
this.logger.trace(String.valueOf(message));
@@ -365,18 +400,6 @@ public abstract class LogFactory {
}
}
- public void warn(Object message) {
- if (message instanceof String || this.logger.isWarnEnabled()) {
- this.logger.warn(String.valueOf(message));
- }
- }
-
- public void warn(Object message, Throwable exception) {
- if (message instanceof String || this.logger.isWarnEnabled()) {
- this.logger.warn(String.valueOf(message), exception);
- }
- }
-
protected Object readResolve() {
return Slf4jDelegate.createLog(this.name);
}
@@ -392,16 +415,12 @@ public abstract class LogFactory {
super(logger);
}
- public void debug(Object message) {
- if (message instanceof String || this.logger.isDebugEnabled()) {
- this.logger.log(null, FQCN, LocationAwareLogger.DEBUG_INT, String.valueOf(message), null, null);
- }
+ public void fatal(Object message) {
+ error(message);
}
- public void debug(Object message, Throwable exception) {
- if (message instanceof String || this.logger.isDebugEnabled()) {
- this.logger.log(null, FQCN, LocationAwareLogger.DEBUG_INT, String.valueOf(message), null, exception);
- }
+ public void fatal(Object message, Throwable exception) {
+ error(message, exception);
}
public void error(Object message) {
@@ -416,12 +435,16 @@ public abstract class LogFactory {
}
}
- public void fatal(Object message) {
- error(message);
+ public void warn(Object message) {
+ if (message instanceof String || this.logger.isWarnEnabled()) {
+ this.logger.log(null, FQCN, LocationAwareLogger.WARN_INT, String.valueOf(message), null, null);
+ }
}
- public void fatal(Object message, Throwable exception) {
- error(message, exception);
+ public void warn(Object message, Throwable exception) {
+ if (message instanceof String || this.logger.isWarnEnabled()) {
+ this.logger.log(null, FQCN, LocationAwareLogger.WARN_INT, String.valueOf(message), null, exception);
+ }
}
public void info(Object message) {
@@ -436,6 +459,18 @@ public abstract class LogFactory {
}
}
+ public void debug(Object message) {
+ if (message instanceof String || this.logger.isDebugEnabled()) {
+ this.logger.log(null, FQCN, LocationAwareLogger.DEBUG_INT, String.valueOf(message), null, null);
+ }
+ }
+
+ public void debug(Object message, Throwable exception) {
+ if (message instanceof String || this.logger.isDebugEnabled()) {
+ this.logger.log(null, FQCN, LocationAwareLogger.DEBUG_INT, String.valueOf(message), null, exception);
+ }
+ }
+
public void trace(Object message) {
if (message instanceof String || this.logger.isTraceEnabled()) {
this.logger.log(null, FQCN, LocationAwareLogger.TRACE_INT, String.valueOf(message), null, null);
@@ -448,18 +483,6 @@ public abstract class LogFactory {
}
}
- public void warn(Object message) {
- if (message instanceof String || this.logger.isWarnEnabled()) {
- this.logger.log(null, FQCN, LocationAwareLogger.WARN_INT, String.valueOf(message), null, null);
- }
- }
-
- public void warn(Object message, Throwable exception) {
- if (message instanceof String || this.logger.isWarnEnabled()) {
- this.logger.log(null, FQCN, LocationAwareLogger.WARN_INT, String.valueOf(message), null, exception);
- }
- }
-
protected Object readResolve() {
return Slf4jDelegate.createLocationAwareLog(this.name);
}
@@ -478,46 +501,30 @@ public abstract class LogFactory {
this.logger = java.util.logging.Logger.getLogger(name);
}
- public boolean isDebugEnabled() {
- return this.logger.isLoggable(java.util.logging.Level.FINE);
+ public boolean isFatalEnabled() {
+ return isErrorEnabled();
}
public boolean isErrorEnabled() {
return this.logger.isLoggable(java.util.logging.Level.SEVERE);
}
- public boolean isFatalEnabled() {
- return this.logger.isLoggable(java.util.logging.Level.SEVERE);
+ public boolean isWarnEnabled() {
+ return this.logger.isLoggable(java.util.logging.Level.WARNING);
}
public boolean isInfoEnabled() {
return this.logger.isLoggable(java.util.logging.Level.INFO);
}
+ public boolean isDebugEnabled() {
+ return this.logger.isLoggable(java.util.logging.Level.FINE);
+ }
+
public boolean isTraceEnabled() {
return this.logger.isLoggable(java.util.logging.Level.FINEST);
}
- public boolean isWarnEnabled() {
- return this.logger.isLoggable(java.util.logging.Level.WARNING);
- }
-
- public void debug(Object message) {
- log(java.util.logging.Level.FINE, message, null);
- }
-
- public void debug(Object message, Throwable exception) {
- log(java.util.logging.Level.FINE, message, exception);
- }
-
- public void error(Object message) {
- log(java.util.logging.Level.SEVERE, message, null);
- }
-
- public void error(Object message, Throwable exception) {
- log(java.util.logging.Level.SEVERE, message, exception);
- }
-
public void fatal(Object message) {
error(message);
}
@@ -526,20 +533,12 @@ public abstract class LogFactory {
error(message, exception);
}
- public void info(Object message) {
- log(java.util.logging.Level.INFO, message, null);
+ public void error(Object message) {
+ log(java.util.logging.Level.SEVERE, message, null);
}
- public void info(Object message, Throwable exception) {
- log(java.util.logging.Level.INFO, message, exception);
- }
-
- public void trace(Object message) {
- log(java.util.logging.Level.FINEST, message, null);
- }
-
- public void trace(Object message, Throwable exception) {
- log(java.util.logging.Level.FINEST, message, exception);
+ public void error(Object message, Throwable exception) {
+ log(java.util.logging.Level.SEVERE, message, exception);
}
public void warn(Object message) {
@@ -550,13 +549,43 @@ public abstract class LogFactory {
log(java.util.logging.Level.WARNING, message, exception);
}
+ public void info(Object message) {
+ log(java.util.logging.Level.INFO, message, null);
+ }
+
+ public void info(Object message, Throwable exception) {
+ log(java.util.logging.Level.INFO, message, exception);
+ }
+
+ public void debug(Object message) {
+ log(java.util.logging.Level.FINE, message, null);
+ }
+
+ public void debug(Object message, Throwable exception) {
+ log(java.util.logging.Level.FINE, message, exception);
+ }
+
+ public void trace(Object message) {
+ log(java.util.logging.Level.FINEST, message, null);
+ }
+
+ public void trace(Object message, Throwable exception) {
+ log(java.util.logging.Level.FINEST, message, exception);
+ }
+
private void log(java.util.logging.Level level, Object message, Throwable exception) {
if (logger.isLoggable(level)) {
- LocationResolvingLogRecord rec = new LocationResolvingLogRecord(level, String.valueOf(message));
- rec.setLoggerName(this.name);
- rec.setResourceBundleName(logger.getResourceBundleName());
- rec.setResourceBundle(logger.getResourceBundle());
- rec.setThrown(exception);
+ LogRecord rec;
+ if (message instanceof LogRecord) {
+ rec = (LogRecord) message;
+ }
+ else {
+ rec = new LocationResolvingLogRecord(level, String.valueOf(message));
+ rec.setLoggerName(this.name);
+ rec.setResourceBundleName(logger.getResourceBundleName());
+ rec.setResourceBundle(logger.getResourceBundle());
+ rec.setThrown(exception);
+ }
logger.log(rec);
}
}
@@ -568,7 +597,7 @@ public abstract class LogFactory {
@SuppressWarnings("serial")
- private static class LocationResolvingLogRecord extends java.util.logging.LogRecord {
+ private static class LocationResolvingLogRecord extends LogRecord {
private static final String FQCN = JavaUtilLog.class.getName();
@@ -622,8 +651,9 @@ public abstract class LogFactory {
setSourceMethodName(sourceMethodName);
}
+ @SuppressWarnings("deprecation") // setMillis is deprecated in JDK 9
protected Object writeReplace() {
- java.util.logging.LogRecord serialized = new java.util.logging.LogRecord(getLevel(), getMessage());
+ LogRecord serialized = new LogRecord(getLevel(), getMessage());
serialized.setLoggerName(getLoggerName());
serialized.setResourceBundle(getResourceBundle());
serialized.setResourceBundleName(getResourceBundleName());
diff --git a/src/docs/asciidoc/overview.adoc b/src/docs/asciidoc/overview.adoc
index 86e1a925d8..45f54d44c4 100644
--- a/src/docs/asciidoc/overview.adoc
+++ b/src/docs/asciidoc/overview.adoc
@@ -41,9 +41,9 @@ Examples of how you, as an application developer, can benefit from the Spring pl
* Make a Java method execute in a database transaction without having to deal with
transaction APIs.
-* Make a local Java method a remote procedure without having to deal with remote APIs.
-* Make a local Java method a management operation without having to deal with JMX APIs.
-* Make a local Java method a message handler without having to deal with JMS APIs.
+* Make a local Java method an HTTP endpoint without having to deal with the Servlet API.
+* Make a local Java method a message handler without having to deal with the JMS API.
+* Make a local Java method a management operation without having to deal with the JMX API.
@@ -606,164 +606,55 @@ http://repo.spring.io/snapshot/org/springframework/spring[snapshots].
[[overview-logging]]
==== Logging
-Logging is a very important dependency for Spring because __a)__ it is the only mandatory
-external dependency, __b)__ everyone likes to see some output from the tools they are
-using, and __c)__ Spring integrates with lots of other tools all of which have also made
-a choice of logging dependency. One of the goals of an application developer is often to
-have unified logging configured in a central place for the whole application, including
-all external components. This is more difficult than it might have been since there are so
-many choices of logging framework.
+Spring's logging setup has been revised for Spring 5: It is still based on the Apache
+Commons Logging API, also known as Jakarta Commons Logging (JCL). However, `spring-core`
+includes an embedded variant of Commons Logging now, with a Spring-specific `LogFactory`
+which automatically bridges to https://logging.apache.org/log4j/2.x/[Log4j 2],
+http://www.slf4j.org[SLF4J], or the JDK's own `java.util.logging` (JUL). This
+implementation acts like the JCL-over-SLF4J bridge but with a range of dynamically
+detected providers, analogous to JBoss Logging's common targets (as used by Hibernate).
-The mandatory logging dependency in Spring is the Jakarta Commons Logging API (JCL). We
-compile against JCL and we also make JCL `Log` objects visible for classes that extend
-the Spring Framework. It's important to users that all versions of Spring use the same
-logging library: migration is easy because backwards compatibility is preserved even
-with applications that extend Spring. The way we do this is to make one of the modules
-in Spring depend explicitly on `commons-logging` (the canonical implementation of JCL),
-and then make all the other modules depend on that at compile time. If you are using
-Maven for example, and wondering where you picked up the dependency on
-`commons-logging`, then it is from Spring and specifically from the central module
-called `spring-core`.
+As a benefit, there is no need for external bridges like JCL-over-SLF4J anymore,
+and correspondingly no need for a manual exclude of the standard Commons Logging jar
+from `spring-core` dependencies. Instead, it all just works in Spring's autodetection
+style at runtime: Simply put Log4j 2.x or SLF4J on your classpath, without any extra
+bridge jars, or rely on default logging through JUL (with a customizable JUL setup).
+And nicely aligned, default Hibernate setup will choose the same common log targets.
-The nice thing about `commons-logging` is that you don't need anything else to make your
-application work. It has a runtime discovery algorithm that looks for other logging
-frameworks in well known places on the classpath and uses one that it thinks is
-appropriate (or you can tell it which one if you need to). If nothing else is available
-you get pretty nice looking logs just from the JDK (java.util.logging or JUL for short).
-You should find that your Spring application works and logs happily to the console out
-of the box in most situations, and that's important.
+If both Log4j and SLF4J are present, the Log4j API will be used preferably (since it
+directly matches JCL's signatures and natively supports a 'fatal' log level as well as
+lazily resolved message objects), analogous to JBoss Logging's provider preferences.
+Log4j may nevertheless be configured to delegate to SLF4J, or SLF4J may be configured
+to delegate to Log4j: Please check the instructions on their websites on how to arrive
+at a consistent outcome in such a mixed scenario.
+[TIP]
+====
+As of Spring 5, drop any references to external Commons Logging bridges and also any
+manual exclude of the standard Commons Logging jar from your existing `spring-core`
+dependency setup. Your Log4j or SLF4J or JUL setup will keep working without changes.
+Note that you may still need a `commons-logging` exclude for other libraries (e.g.
+Apache HttpClient, Castor, HtmlUnit) in order to pick up Spring's JCL bridge instead.
-[[overview-not-using-commons-logging]]
-===== Not Using Commons Logging
-Unfortunately, the runtime discovery algorithm in `commons-logging`, while convenient
-for the end-user, is problematic. If we could turn back the clock and start Spring now
-as a new project it would use a different logging dependency. The first choice would
-probably be the Simple Logging Facade for Java ( http://www.slf4j.org[SLF4J]), which is
-also used by a lot of other tools that people use with Spring inside their applications.
-
-There are basically two ways to switch off `commons-logging`:
-
-. Exclude the dependency from the `spring-core` module (as it is the only module that
- explicitly depends on `commons-logging`)
-. Depend on a special `commons-logging` dependency that replaces the library with
- an empty jar (more details can be found in the
- http://slf4j.org/faq.html#excludingJCL[SLF4J FAQ])
-
-To exclude commons-logging, add the following to your `dependencyManagement` section:
-
-[source,xml,indent=0]
-[subs="verbatim,quotes,attributes"]
-----
-