Log4j日志输出详解 : http://donald-draper.iteye.com/blog/2332395
slf4j + Log4j的使用: http://donald-draper.iteye.com/blog/2332407
上一篇讲了slf4j + Log4j的使用,其中获取Logger的一句为
private static Logger log = LoggerFactory.getLogger(testSFL4J.class);
下面来看这一句的具体含义:
public final class LoggerFactory { static final String CODES_PREFIX = "http://www.slf4j.org/codes.html"; static final String NO_STATICLOGGERBINDER_URL = "http://www.slf4j.org/codes.html#StaticLoggerBinder"; static final String MULTIPLE_BINDINGS_URL = "http://www.slf4j.org/codes.html#multiple_bindings"; static final String NULL_LF_URL = "http://www.slf4j.org/codes.html#null_LF"; static final String VERSION_MISMATCH = "http://www.slf4j.org/codes.html#version_mismatch"; static final String SUBSTITUTE_LOGGER_URL = "http://www.slf4j.org/codes.html#substituteLogger"; static final String LOGGER_NAME_MISMATCH_URL = "http://www.slf4j.org/codes.html#loggerNameMismatch"; static final String UNSUCCESSFUL_INIT_URL = "http://www.slf4j.org/codes.html#unsuccessfulInit"; static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory could not be successfully initialized. See also http://www.slf4j.org/codes.html#unsuccessfulInit"; static final int UNINITIALIZED = 0; static final int ONGOING_INITIALIZATION = 1; static final int FAILED_INITIALIZATION = 2; static final int SUCCESSFUL_INITIALIZATION = 3; static final int NOP_FALLBACK_INITIALIZATION = 4; static int INITIALIZATION_STATE = 0; static SubstituteLoggerFactory TEMP_FACTORY = new SubstituteLoggerFactory(); static NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory(); static final String DETECT_LOGGER_NAME_MISMATCH_PROPERTY = "slf4j.detectLoggerNameMismatch"; static boolean DETECT_LOGGER_NAME_MISMATCH = Boolean.getBoolean("slf4j.detectLoggerNameMismatch"); private static final String API_COMPATIBILITY_LIST[] = { "1.6", "1.7" }; private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class"; //根据class,获取Logger public static Logger getLogger(Class clazz) { Logger logger = getLogger(clazz.getName()); if(DETECT_LOGGER_NAME_MISMATCH) { Class autoComputedCallingClass = Util.getCallingClass(); if(nonMatchingClasses(clazz, autoComputedCallingClass)) { Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", new Object[] { logger.getName(), autoComputedCallingClass.getName() })); Util.report("See http://www.slf4j.org/codes.html#loggerNameMismatch for an explanation"); } } return logger; } //根据class name 获取Logger public static Logger getLogger(String name) { //获取LoggerFactory ILoggerFactory iLoggerFactory = getILoggerFactory(); //从LoggerFactory获取Logger return iLoggerFactory.getLogger(name); } //获取LoggerFactory public static ILoggerFactory getILoggerFactory() { if(INITIALIZATION_STATE == 0) { INITIALIZATION_STATE = 1; //执行初始化 performInitialization(); } switch(INITIALIZATION_STATE) { case 3: // '\003' //返回Log4jLoggerFactory return StaticLoggerBinder.getSingleton().getLoggerFactory(); case 4: // '\004' //如果状态为4,则返回NOPLoggerFactory return NOP_FALLBACK_FACTORY; case 2: // '\002' throw new IllegalStateException("org.slf4j.LoggerFactory could not be successfully initialized. See also http://www.slf4j.org/codes.html#unsuccessfulInit"); //如果初始化状态为1,返回临时LoggerFactory,SubstituteLoggerFactory case 1: // '\001' return TEMP_FACTORY; } throw new IllegalStateException("Unreachable code"); } //执行初始化 private static final void performInitialization() { //绑定日志工场 bind(); if(INITIALIZATION_STATE == 3) //验证配置 versionSanityCheck(); } //绑定日志工场 private static final void bind() { try { //获取具体LoggerBinder类,默认为StaticLoggerBinder,在slf4j-log4j12-1.7.9.jar 中 Set staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); reportMultipleBindingAmbiguity(staticLoggerBinderPathSet); StaticLoggerBinder.getSingleton(); INITIALIZATION_STATE = 3; reportActualBinding(staticLoggerBinderPathSet); //从SubstituteLoggerFactory获取具体sfl4j的Logger实现,并用SubstituteLogger代理类去包装 fixSubstitutedLoggers(); } catch(NoClassDefFoundError ncde) { String msg = ncde.getMessage(); if(messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) { //初始化状态为4,返回NOPLoggerFactory,无配置Logger INITIALIZATION_STATE = 4; Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\"."); Util.report("Defaulting to no-operation (NOP) logger implementation"); Util.report("See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details."); } else { failedBinding(ncde); throw ncde; } } catch(NoSuchMethodError nsme) { String msg = nsme.getMessage(); if(msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) { //org.slf4j.LoggerFactory could not be successfully initialized INITIALIZATION_STATE = 2; Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding."); Util.report("Your binding is version 1.5.5 or earlier."); Util.report("Upgrade your binding to version 1.6.x."); } throw nsme; } catch(Exception e) { failedBinding(e); throw new IllegalStateException("Unexpected initialization failure", e); } } //获取StaticLoggerBinderPathS private static Set findPossibleStaticLoggerBinderPathSet() { Set staticLoggerBinderPathSet = new LinkedHashSet(); try { ClassLoader loggerFactoryClassLoader = org/slf4j/LoggerFactory.getClassLoader(); Enumeration paths; if(loggerFactoryClassLoader == null) paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH); else // private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class"; paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH); URL path; for(; paths.hasMoreElements(); staticLoggerBinderPathSet.add(path)) path = (URL)paths.nextElement(); } return staticLoggerBinderPathSet; } // StaticLoggerBinder,是否有多个实现版本 private static boolean isAmbiguousStaticLoggerBinderPathSet(Set staticLoggerBinderPathSet) { return staticLoggerBinderPathSet.size() > 1; } //report多StaticLoggerBinder,并打印具体实现的package,jar地址 private static void reportMultipleBindingAmbiguity(Set staticLoggerBinderPathSet) { if(isAmbiguousStaticLoggerBinderPathSet(staticLoggerBinderPathSet)) { Util.report("Class path contains multiple SLF4J bindings."); URL path; for(Iterator iterator = staticLoggerBinderPathSet.iterator(); iterator.hasNext(); Util.report((new StringBuilder()).append("Found binding in [").append(path).append("]").toString())) path = (URL)iterator.next(); Util.report("See http://www.slf4j.org/codes.html#multiple_bindings for an explanation."); } } //report实际绑定,实际绑定的StaticLoggerBinder,是随机的。 private static void reportActualBinding(Set staticLoggerBinderPathSet) { if(isAmbiguousStaticLoggerBinderPathSet(staticLoggerBinderPathSet)) Util.report((new StringBuilder()).append("Actual binding is of type [").append(StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr()).append("]").toString()); } //从SubstituteLoggerFactory获取具体sfl4j的Logger实现,并用SubstituteLogger代理类去包装 //如果日志Logger为sfl4j的Logger的实现,则用SubstituteLogger去代理具体的实现Logger private static final void fixSubstitutedLoggers() { //从临时工厂SubstituteLoggerFactory,获取所有Loggers List loggers = TEMP_FACTORY.getLoggers(); if(loggers.isEmpty()) return; Util.report("The following set of substitute loggers may have been accessed"); Util.report("during the initialization phase. Logging calls during this"); Util.report("phase were not honored. However, subsequent logging calls to these"); Util.report("loggers will work as normally expected."); Util.report("See also http://www.slf4j.org/codes.html#substituteLogger"); SubstituteLogger subLogger; for(Iterator i$ = loggers.iterator(); i$.hasNext(); Util.report(subLogger.getName())) { subLogger = (SubstituteLogger)i$.next(); subLogger.setDelegate(getLogger(subLogger.getName())); } TEMP_FACTORY.clear(); } //验证配置,校验文件 private static final void versionSanityCheck() { try { String requested = StaticLoggerBinder.REQUESTED_API_VERSION; boolean match = false; for(int i = 0; i < API_COMPATIBILITY_LIST.length; i++) if(requested.startsWith(API_COMPATIBILITY_LIST[i])) match = true; if(!match) { Util.report((new StringBuilder()).append("The requested version ").append(requested).append(" by your slf4j binding is not compatible with ").append(Arrays.asList(API_COMPATIBILITY_LIST).toString()).toString()); Util.report("See http://www.slf4j.org/codes.html#version_mismatch for further details."); } } } }
下面来看一下StaticLoggerBinder
public class StaticLoggerBinder implements LoggerFactoryBinder { public static final StaticLoggerBinder getSingleton() { return SINGLETON; } private StaticLoggerBinder() { Level level; try { level = Level.TRACE; } } public ILoggerFactory getLoggerFactory() { return loggerFactory; } public String getLoggerFactoryClassStr() { return loggerFactoryClassStr; } private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder(); public static String REQUESTED_API_VERSION = "1.6.99"; private static final String loggerFactoryClassStr = org/slf4j/impl/Log4jLoggerFactory.getName(); //从这里可以看出,返回的为Log4jLoggerFactory private final ILoggerFactory loggerFactory = new Log4jLoggerFactory(); }
再看Log4jLoggerFactory
//Log4jLoggerFactory public class Log4jLoggerFactory implements ILoggerFactory { public Log4jLoggerFactory() { loggerMap = new ConcurrentHashMap(); } public Logger getLogger(String name) { //如果有slf4jLogger具体实现,则返回 Logger slf4jLogger = (Logger)loggerMap.get(name); if(slf4jLogger != null) return slf4jLogger; org.apache.log4j.Logger log4jLogger; if(name.equalsIgnoreCase("ROOT")) log4jLogger = LogManager.getRootLogger(); else //看到这一句是不是很熟悉,LogManager,初始配置,获取log4jLogger //这个具体的我们在log4j初始化,那篇文章中有讲 log4jLogger = LogManager.getLogger(name); //将log4jLogger包装成slf4jLogger Logger newInstance = new Log4jLoggerAdapter(log4jLogger); Logger oldInstance = (Logger)loggerMap.putIfAbsent(name, newInstance); return oldInstance != null ? oldInstance : newInstance; } ConcurrentMap loggerMap; }
//Log4jLoggerAdapter
import java.io.Serializable; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.slf4j.Marker; import org.slf4j.helpers.*; import org.slf4j.spi.LocationAwareLogger; public final class Log4jLoggerAdapter extends MarkerIgnoringBase implements LocationAwareLogger, Serializable { Log4jLoggerAdapter(Logger logger) { this.logger = logger; name = logger.getName(); } public void info(String msg) { //这里就是直接调用Log4J的log INFO logger.log(FQCN, Level.INFO, msg, null); } private static final long serialVersionUID = 6182834493563598289L; final transient Logger logger; static final String FQCN = org/slf4j/impl/Log4jLoggerAdapter.getName(); final boolean traceCapable = isTraceCapable(); }
//LocationAwareLogger
import org.slf4j.Logger; import org.slf4j.Marker; public interface LocationAwareLogger extends Logger { public abstract void log(Marker marker, String s, int i, String s1, Object aobj[], Throwable throwable); public static final int TRACE_INT = 0; public static final int DEBUG_INT = 10; public static final int INFO_INT = 20; public static final int WARN_INT = 30; public static final int ERROR_INT = 40; }
//SubstituteLogger,Logger具体实现的代理类
public class SubstituteLogger implements Logger { public void setDelegate(Logger delegate) { _delegate = delegate; } private final String name; private volatile Logger _delegate; }
当StaticLoggerBinder,找不到默认的Log4J,则从临时工厂SubstituteLoggerFactory,去获取
合适的Logger
//SubstituteLoggerFactory import org.slf4j.ILoggerFactory; import org.slf4j.Logger; public class SubstituteLoggerFactory implements ILoggerFactory { final ConcurrentMap loggers = new ConcurrentHashMap(); //从SubstituteLoggerFactory或sfl4j的具体实现Logger,如果工厂中不存在, //则新建一个Logger,并放入SubstituteLoggerFactory中 public Logger getLogger(String name) { SubstituteLogger logger = (SubstituteLogger)loggers.get(name); if(logger == null) { logger = new SubstituteLogger(name); SubstituteLogger oldLogger = (SubstituteLogger)loggers.putIfAbsent(name, logger); if(oldLogger != null) logger = oldLogger; } return logger; } }
下面来看一下:NOPLoggerFactory
//NOPLoggerFactory public class NOPLoggerFactory implements ILoggerFactory { public NOPLoggerFactory() { } public Logger getLogger(String name) { return NOPLogger.NOP_LOGGER; } } //NOPLogger public class NOPLogger extends MarkerIgnoringBase { public static final NOPLogger NOP_LOGGER = new NOPLogger(); }
//MarkerIgnoringBase
import org.slf4j.Logger; import org.slf4j.Marker; public abstract class MarkerIgnoringBase extends NamedLoggerBase implements Logger { public void info(Marker marker, String msg) { info(msg); } public void info(Marker marker, String format, Object arg) { info(format, arg); } public void info(Marker marker, String format, Object arg1, Object arg2) { info(format, arg1, arg2); } public transient void info(Marker marker, String format, Object arguments[]) { info(format, arguments); } public void info(Marker marker, String msg, Throwable t) { info(msg, t); } public volatile String getName() { return super.getName(); } }
测试当有多个StaticLoggerBinder具体实现,实际bind,添加多个具体实现jar包,控制台会输出:
第一种情况:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/E:/lib/logback-classic-0.9.27.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/E:/lib/slf4j-log4j12-1.7.9.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
####实际绑定为logback-classic-0.9.27.jar的StaticLoggerBinder
SLF4J: Actual binding is of type [ch.qos.logback.classic.selector.DefaultContextSelector]
==================================================================
第二种情况:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/E:/lib/slf4j-log4j12-1.7.9.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/E:/lib/logback-classic-0.9.27.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
####实际绑定为slf4j-log4j12-1.7.9.jar的StaticLoggerBinder
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
从输出信息可以看出,slf4j 实际bind StaticLoggerBinder,以java build path 中引用的jar的顺序有关,测试结果是第一个发现的jar。
总结:
从上面可以分析,可以看出sfl4j,通过LoggerFactory,来获取实际的Logger实现,默认会找Log4jLoggerFactory,从Log4jLoggerFactory获取log4jLogger,具体为log4jLogger的适配器Log4jLoggerAdapter。当无法找到Log4jLoggerFactory,则从SubstituteLoggerFactory,获取sfl4j的具体实现,并通过SubstituteLogger取代;如果无法找到sfl4j的具体实现,则从NOPLoggerFactory获取Logger。当Logger为Log4jLoggerAdapter时,则调用log4j的日志打印;当Logger为SubstituteLogger,则通过SubstituteLogger,打印日志;如果上面两种,都没有则使用NOPLogger,去打印日志。