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
This commit is contained in:
@@ -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).
|
||||
*
|
||||
* <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;
|
||||
* 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 {
|
||||
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());
|
||||
|
||||
@@ -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"]
|
||||
----
|
||||
<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.
|
||||
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
|
||||
===== Using Log4j 2.x
|
||||
|
||||
NOTE: Log4j 1.x is EOL and the following applies to Log4j 2
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
<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:
|
||||
<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:
|
||||
</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.
|
||||
|
||||
Reference in New Issue
Block a user