Browse Source

Suppress deprecation warning on JDK 9 (for LogRecord.setMillis)

Includes revised Log methods in log level order, with consistent delegation of fatal->error for log level checks in SLF4J and JUL, a JavaUtilDelegate for defensive access to java.logging on JDK 9, support for LogRecord message objects, as well as revised log setup recommendations.

Issue: SPR-15453
Issue: SPR-14512
pull/1392/head
Juergen Hoeller 9 years ago
parent
commit
d50dab6ea9
  1. 276
      spring-core/src/main/java/org/apache/commons/logging/LogFactory.java
  2. 292
      src/docs/asciidoc/overview.adoc

276
spring-core/src/main/java/org/apache/commons/logging/LogFactory.java

@ -17,6 +17,7 @@ @@ -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; @@ -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).
*
* <p>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
* <p>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; @@ -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.
*
* <p><b>Note that this Commons Logging variant is only meant to be used for
* framework logging purposes, both in the core framework and in extensions.</b>
* For applications, prefer direct use of Log4J or SLF4J or {@code java.util.logging}.
* infrastructure logging purposes in the core framework and in extensions.</b>
* 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.
*
* <p><b>For logging need in application code, prefer direct use of Log4j 2.x
* or SLF4J or {@code java.util.logging}.</b> 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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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
@ -202,23 +225,23 @@ public abstract class LogFactory { @@ -202,23 +225,23 @@ public abstract class LogFactory {
}
@Override
public boolean isTraceEnabled() {
return logger.isEnabled(Level.TRACE, null, null);
public boolean isDebugEnabled() {
return logger.isEnabled(Level.DEBUG, null, null);
}
@Override
public boolean isWarnEnabled() {
return logger.isEnabled(Level.WARN, null, null);
public boolean isTraceEnabled() {
return logger.isEnabled(Level.TRACE, null, null);
}
@Override
public void debug(Object message) {
logger.logIfEnabled(FQCN, Level.DEBUG, null, message, null);
public void fatal(Object message) {
logger.logIfEnabled(FQCN, Level.FATAL, null, message, null);
}
@Override
public void debug(Object message, Throwable exception) {
logger.logIfEnabled(FQCN, Level.DEBUG, null, message, exception);
public void fatal(Object message, Throwable exception) {
logger.logIfEnabled(FQCN, Level.FATAL, null, message, exception);
}
@Override
@ -232,13 +255,13 @@ public abstract class LogFactory { @@ -232,13 +255,13 @@ public abstract class LogFactory {
}
@Override
public void fatal(Object message) {
logger.logIfEnabled(FQCN, Level.FATAL, null, message, null);
public void warn(Object message) {
logger.logIfEnabled(FQCN, Level.WARN, null, message, null);
}
@Override
public void fatal(Object message, Throwable exception) {
logger.logIfEnabled(FQCN, Level.FATAL, null, message, exception);
public void warn(Object message, Throwable exception) {
logger.logIfEnabled(FQCN, Level.WARN, null, message, exception);
}
@Override
@ -252,23 +275,23 @@ public abstract class LogFactory { @@ -252,23 +275,23 @@ public abstract class LogFactory {
}
@Override
public void trace(Object message) {
logger.logIfEnabled(FQCN, Level.TRACE, null, message, null);
public void debug(Object message) {
logger.logIfEnabled(FQCN, Level.DEBUG, null, message, null);
}
@Override
public void trace(Object message, Throwable exception) {
logger.logIfEnabled(FQCN, Level.TRACE, null, message, exception);
public void debug(Object message, Throwable exception) {
logger.logIfEnabled(FQCN, Level.DEBUG, null, message, exception);
}
@Override
public void warn(Object message) {
logger.logIfEnabled(FQCN, Level.WARN, null, message, null);
public void trace(Object message) {
logger.logIfEnabled(FQCN, Level.TRACE, null, message, null);
}
@Override
public void warn(Object message, Throwable exception) {
logger.logIfEnabled(FQCN, Level.WARN, null, message, exception);
public void trace(Object message, Throwable exception) {
logger.logIfEnabled(FQCN, Level.TRACE, null, message, exception);
}
}
@ -285,40 +308,36 @@ public abstract class LogFactory { @@ -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 isTraceEnabled() {
return this.logger.isTraceEnabled();
public boolean isDebugEnabled() {
return this.logger.isDebugEnabled();
}
public boolean isWarnEnabled() {
return this.logger.isWarnEnabled();
public boolean isTraceEnabled() {
return this.logger.isTraceEnabled();
}
public void debug(Object message) {
if (message instanceof String || this.logger.isDebugEnabled()) {
this.logger.debug(String.valueOf(message));
}
public void fatal(Object message) {
error(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 { @@ -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,27 +376,27 @@ public abstract class LogFactory { @@ -353,27 +376,27 @@ public abstract class LogFactory {
}
}
public void trace(Object message) {
if (message instanceof String || this.logger.isTraceEnabled()) {
this.logger.trace(String.valueOf(message));
public void debug(Object message) {
if (message instanceof String || this.logger.isDebugEnabled()) {
this.logger.debug(String.valueOf(message));
}
}
public void trace(Object message, Throwable exception) {
if (message instanceof String || this.logger.isTraceEnabled()) {
this.logger.trace(String.valueOf(message), exception);
public void debug(Object message, Throwable exception) {
if (message instanceof String || this.logger.isDebugEnabled()) {
this.logger.debug(String.valueOf(message), exception);
}
}
public void warn(Object message) {
if (message instanceof String || this.logger.isWarnEnabled()) {
this.logger.warn(String.valueOf(message));
public void trace(Object message) {
if (message instanceof String || this.logger.isTraceEnabled()) {
this.logger.trace(String.valueOf(message));
}
}
public void warn(Object message, Throwable exception) {
if (message instanceof String || this.logger.isWarnEnabled()) {
this.logger.warn(String.valueOf(message), exception);
public void trace(Object message, Throwable exception) {
if (message instanceof String || this.logger.isTraceEnabled()) {
this.logger.trace(String.valueOf(message), exception);
}
}
@ -392,16 +415,12 @@ public abstract class LogFactory { @@ -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 { @@ -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,27 +459,27 @@ public abstract class LogFactory { @@ -436,27 +459,27 @@ public abstract class LogFactory {
}
}
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);
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 trace(Object message, Throwable exception) {
if (message instanceof String || this.logger.isTraceEnabled()) {
this.logger.log(null, FQCN, LocationAwareLogger.TRACE_INT, String.valueOf(message), null, exception);
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 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 trace(Object message) {
if (message instanceof String || this.logger.isTraceEnabled()) {
this.logger.log(null, FQCN, LocationAwareLogger.TRACE_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);
public void trace(Object message, Throwable exception) {
if (message instanceof String || this.logger.isTraceEnabled()) {
this.logger.log(null, FQCN, LocationAwareLogger.TRACE_INT, String.valueOf(message), null, exception);
}
}
@ -478,36 +501,36 @@ public abstract class LogFactory { @@ -478,36 +501,36 @@ 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 isTraceEnabled() {
return this.logger.isLoggable(java.util.logging.Level.FINEST);
public boolean isDebugEnabled() {
return this.logger.isLoggable(java.util.logging.Level.FINE);
}
public boolean isWarnEnabled() {
return this.logger.isLoggable(java.util.logging.Level.WARNING);
public boolean isTraceEnabled() {
return this.logger.isLoggable(java.util.logging.Level.FINEST);
}
public void debug(Object message) {
log(java.util.logging.Level.FINE, message, null);
public void fatal(Object message) {
error(message);
}
public void debug(Object message, Throwable exception) {
log(java.util.logging.Level.FINE, message, exception);
public void fatal(Object message, Throwable exception) {
error(message, exception);
}
public void error(Object message) {
@ -518,12 +541,12 @@ public abstract class LogFactory { @@ -518,12 +541,12 @@ public abstract class LogFactory {
log(java.util.logging.Level.SEVERE, message, exception);
}
public void fatal(Object message) {
error(message);
public void warn(Object message) {
log(java.util.logging.Level.WARNING, message, null);
}
public void fatal(Object message, Throwable exception) {
error(message, exception);
public void warn(Object message, Throwable exception) {
log(java.util.logging.Level.WARNING, message, exception);
}
public void info(Object message) {
@ -534,29 +557,35 @@ public abstract class LogFactory { @@ -534,29 +557,35 @@ public abstract class LogFactory {
log(java.util.logging.Level.INFO, message, exception);
}
public void trace(Object message) {
log(java.util.logging.Level.FINEST, message, null);
public void debug(Object message) {
log(java.util.logging.Level.FINE, message, null);
}
public void trace(Object message, Throwable exception) {
log(java.util.logging.Level.FINEST, message, exception);
public void debug(Object message, Throwable exception) {
log(java.util.logging.Level.FINE, message, exception);
}
public void warn(Object message) {
log(java.util.logging.Level.WARNING, message, null);
public void trace(Object message) {
log(java.util.logging.Level.FINEST, message, null);
}
public void warn(Object message, Throwable exception) {
log(java.util.logging.Level.WARNING, message, exception);
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 { @@ -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 { @@ -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());

292
src/docs/asciidoc/overview.adoc

@ -41,9 +41,9 @@ Examples of how you, as an application developer, can benefit from the Spring pl @@ -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]. @@ -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.
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`.
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.
[[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"]
----
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>{spring-version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
----
Now this application is probably broken because there is no implementation of the JCL
API on the classpath, so to fix it a new one has to be provided. In the next section we
show you how to provide an alternative implementation of JCL using SLF4J as an example.
[[overview-logging-slf4j]]
===== Using SLF4J
SLF4J is a cleaner dependency and more efficient at runtime than `commons-logging`
because it uses compile-time bindings instead of runtime discovery of the other logging
frameworks it integrates. This also means that you have to be more explicit about what
you want to happen at runtime, and declare it or configure it accordingly. SLF4J
provides bindings to many common logging frameworks, so you can usually choose one that
you already use, and bind to that for configuration and management.
SLF4J provides bindings to many common logging frameworks, including JCL, and it also
does the reverse: bridges between other logging frameworks and itself. So to use SLF4J
with Spring you need to replace the `commons-logging` dependency with the SLF4J-JCL
bridge. Once you have done that then logging calls from within Spring will be translated
into logging calls to the SLF4J API, so if other libraries in your application use that
API, then you have a single place to configure and manage logging.
A common choice might be to bridge Spring to SLF4J, and then provide explicit binding
from SLF4J to Log4j. You need to supply several dependencies (and exclude the existing
`commons-logging`): the bridge, the SLF4J implementation for Log4j, and the Log4j
implementation itself. In Maven you would do that like this:
[source,xml,indent=0]
[subs="verbatim,quotes,attributes"]
----
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>{spring-version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.22</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.7</version>
</dependency>
</dependencies>
----
That might seem like a lot of dependencies just to get some logging. Well it is, but it
__is__ optional, and it should behave better than the vanilla `commons-logging` with
respect to classloader issues, notably if you are in a strict container like an OSGi
platform. Allegedly there is also a performance benefit because the bindings are at
compile-time not runtime.
A more common choice amongst SLF4J users, which uses fewer steps and generates fewer
dependencies, is to bind directly to http://logback.qos.ch[Logback]. This removes the
extra binding step because Logback implements SLF4J directly, so you only need to depend
on two libraries not four ( `jcl-over-slf4j` and `logback`). If you do that you might
also need to exclude the slf4j-api dependency from other external dependencies (not
Spring), because you only want one version of that API on the classpath.
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).
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.
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.
A custom `LogFactory` implementation at the Commons Logging level will NOT get picked
up since Spring's bridge does not support custom `commons-logging.properties' setup.
For any other log provider, please set up corresponding SLF4J or JUL bridges (which
you are very likely going to need for other libraries such as Hibernate anyway).
Note that Log4j 1.x has reached its end-of-life; please migrate to Log4j 2.x.
====
[[overview-logging-log4j]]
===== Using Log4j
NOTE: Log4j 1.x is EOL and the following applies to Log4j 2
===== Using Log4j 2.x
Many people use http://logging.apache.org/log4j[Log4j] as a logging framework for
configuration and management purposes. It's efficient and well-established, and in fact
it's what we use at runtime when we build and test Spring. Spring also provides some
utilities for configuring and initializing Log4j, so it has an optional compile-time
dependency on Log4j in some modules.
http://logging.apache.org/log4j[Log4j 2] established itself as a fresh rewrite of
the original Log4j project (1.x is EOL now). As of Spring 5, the embedded logging
bridge will automatically delegate to Log4j 2.x when available on the classpath.
To use Log4j with JCL, all you need to do is put Log4j on the classpath and provide
it with a configuration file (`log4j2.xml`, `log4j2.properties`, or other
So to use Log4j with Spring, all you need to do is put Log4j on the classpath and
provide it with a configuration file (`log4j2.xml`, `log4j2.properties`, or other
http://logging.apache.org/log4j/2.x/manual/configuration.html[supported configuration
formats]). For Maven users, the minimal dependencies needed are:
formats]). For Maven users, the minimal dependency needed is:
[source,xml,indent=0]
[subs="verbatim,quotes,attributes"]
@ -772,17 +663,13 @@ formats]). For Maven users, the minimal dependencies needed are: @@ -772,17 +663,13 @@ formats]). For Maven users, the minimal dependencies needed are:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>2.7</version>
<version>2.8.2</version>
</dependency>
</dependencies>
----
If you also wish to use SLF4J, the following dependencies are also needed:
If you also wish to enable SLF4J to delegate to Log4j, e.g. for other libraries
which use SLF4J by default, the following dependency is also needed:
[source,xml,indent=0]
[subs="verbatim,quotes,attributes"]
@ -791,7 +678,7 @@ If you also wish to use SLF4J, the following dependencies are also needed: @@ -791,7 +678,7 @@ If you also wish to use SLF4J, the following dependencies are also needed:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.7</version>
<version>2.8.2</version>
</dependency>
</dependencies>
----
@ -817,25 +704,68 @@ Here is an example `log4j2.xml` for logging to the console: @@ -817,25 +704,68 @@ Here is an example `log4j2.xml` for logging to the console:
</Configuration>
----
[[overview-native-jcl]]
====== Runtime Containers with Native JCL
Many people run their Spring applications in a container that itself provides an
implementation of JCL. IBM Websphere Application Server (WAS) is the archetype. This
often causes problems, and unfortunately there is no silver bullet solution; simply
excluding `commons-logging` from your application is not enough in most situations.
To be clear about this: the problems reported are usually not with JCL per se, or even
with `commons-logging`: rather they are to do with binding `commons-logging` to another
framework (often Log4j). This can fail because `commons-logging` changed the way they do
the runtime discovery in between the older versions (1.0) found in some containers and
the modern versions that most people use now (1.1). Spring does not use any unusual
parts of the JCL API, so nothing breaks there, but as soon as Spring or your application
tries to do any logging you can find that the bindings to Log4j are not working.
[[overview-logging-slf4j]]
===== Using SLF4J with Logback
In such cases with WAS the easiest thing to do is to invert the class loader hierarchy
(IBM calls it "parent last") so that the application controls the JCL dependency, not
the container. That option isn't always open, but there are plenty of other suggestions
in the public domain for alternative approaches, and your mileage may vary depending on
the exact version and feature set of the container.
The Simple Logging Facade for Java (http://www.slf4j.org[SLF4J]) is a popular API
used by other libraries commonly used with Spring. It is typically used with
https://logback.qos.ch/[Logback] which is a native implementation of the SLF4J API
and therefore autodetected by Spring when added to the application classpath:
[source,xml,indent=0]
[subs="verbatim,quotes,attributes"]
----
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.2</version>
</dependency>
</dependencies>
----
Alternatively, you may also configure SLF4J to delegate to Log4j (see above) or to
JUL, in particular for other libraries which use SLF4J by default. Note that it is
not important for all libraries to go through the same logging facade; it only
matters that they eventually delegate to the same log provider. So while Spring may
go to Log4j directly, other libraries may go through the SLF4J binding for Log4j,
or analogously for JUL.
[[overview-logging-jul]]
===== Using JUL (java.util.logging)
Spring will delegate to `java.util.logging` by default, provided that no Log4j or
SLF4J API is detected in the classpath. So there is no special dependency to set up:
just use Spring with no external dependency for log output to `java.util.logging`,
either in a standalone application (with a custom or default JUL setup at the JDK
level) or with an application server's log system (and its system-wide JUL setup).
Note that the `java.logging` module is NOT present by default on JDK 9, since it is
not included in `java.base`. This works fine when using Spring with Log4j or SLF4J
since the JUL API is not referenced in such a scenario. However, when choosing to
use JUL as a default log provider, remember to activate the `java.logging` module.
[[overview-logging-websphere]]
===== Commons Logging on WebSphere
Spring applications may run on a container that itself provides an implementation of
JCL, e.g. IBM's WebSphere Application Server (WAS). This does not cause issues per se
but leads to two different scenarios that need to be understood:
In a "parent first" ClassLoader delegation model (the default on WAS), applications
will always pick up the server-provided version of Commons Logging, delegating to the
WAS logging subsystem (which is actually based on JUL). An application-provided variant
of JCL, whether Spring 5's or the JCL-over-SLF4J bridge, will effectively be ignored,
along with any locally included log provider.
With a "parent last" delegation model (the default in a regular Servlet container but
an explicit configuration option on WAS), an application-provided Commons Logging
variant will be picked up, enabling you to set up a locally included log provider,
e.g. Log4j or Logback, within your application. In case of no local log provider,
Spring (like regular Commons Logging) will delegate to JUL by default, effectively
logging to WebSphere's logging subsystem like in the "parent first" scenario.
All in all, we recommend deploying Spring applications in the "parent last" model
since it naturally allows for local providers as well as the server's log subsystem.

Loading…
Cancel
Save