slf4j日誌框架綁定機制
一、環境搭建
我們以log4j為例,探尋slf4j與log4j的綁定過程。
1.Java類
public class Slf4jBind { public static void main(String[] args) { Logger LOGGER = LoggerFactory.getLogger(Slf4jBind.class); LOGGER.info("slf4j hello world"); } }
2.log4j.properties文件
來自https://docs.oracle.com/cd/E29578_01/webhelp/cas_webcrawler/src/cwcg_config_log4j_file.html
log4j.rootLogger=ERROR,stdout log4j.logger.com.endeca=INFO # Logger for crawl metrics log4j.logger.com.endeca.itl.web.metrics=INFO ? log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%p\t%d{ISO8601}\t%r\t%c\t[%t]\t%m%n
3.Maven的pom.xml中添加如下依賴
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.4</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.6.4</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> </dependency>
其中,slf4j-api定義接口的包。
slf4j-log4j12為slf4j與logj之間的橋接包。
log4j為log4j的具體實現包。
4.單步跟蹤
在main函數處打斷點,debug模式下單步跟蹤即可
二、slf4j綁定log4j流程
main函數中調用LoggerFactory.getLogger時,將依次執行下面的函數。
slf4j對LoggerFactroy的說明如下:
LoggerFactory是為各個日誌API生成Logger的幫助類。
如log4j,logback,jdk 1.4 logging,也支持NOPLogger,SimpleLogger。
LoggerFactory內部封裝了ILoggerFactory,具體的ILoggerFactory與LoggerFactory在編譯期(complile time)綁定。
public static Logger getLogger(Class clazz) { return getLogger(clazz.getName()); } public static Logger getLogger(String name) { ILoggerFactory iLoggerFactory = getILoggerFactory(); return iLoggerFactory.getLogger(name); } ? / * ILoggerFactory instance is bound with this class at compile time. * * @return the ILoggerFactory instance in use */ public static ILoggerFactory getILoggerFactory() { if (INITIALIZATION_STATE == UNINITIALIZED) { INITIALIZATION_STATE = ONGOING_INITILIZATION; performInitialization(); ? } switch (INITIALIZATION_STATE) { case SUCCESSFUL_INITILIZATION: return StaticLoggerBinder.getSingleton().getLoggerFactory(); case NOP_FALLBACK_INITILIZATION: return NOP_FALLBACK_FACTORY; case FAILED_INITILIZATION: throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG); case ONGOING_INITILIZATION: // support re-entrant behavior. // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106 return TEMP_FACTORY; } throw new IllegalStateException("Unreachable code"); }
private final static void performInitialization() { //對具體的日誌框架實現是否僅有一個進行check singleImplementationSanityCheck(); //與具體的日誌框架綁定 bind(); if (INITIALIZATION_STATE == SUCCESSFUL_INITILIZATION) { //版本依賴兼容性check versionSanityCheck(); } }
1.對具體的日誌框架實現是否僅有一個進行check。
1).判斷classpath下是否有多個org/slf4j/impl/StaticLoggerBinder.class
2).如果有多個,將提示有多個SLF4J的綁定包。
private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class"; ? private static void singleImplementationSanityCheck() { try { ClassLoader loggerFactoryClassLoader = LoggerFactory.class .getClassLoader(); Enumeration paths; if (loggerFactoryClassLoader == null) { paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH); } else { paths = loggerFactoryClassLoader .getResources(STATIC_LOGGER_BINDER_PATH); } // use Set instead of list in order to deal with bug #138 // LinkedHashSet appropriate here because it preserves insertion order during iteration Set implementationSet = new LinkedHashSet(); while (paths.hasMoreElements()) { URL path = (URL) paths.nextElement(); implementationSet.add(path); } if (implementationSet.size() > 1) { Util.report("Class path contains multiple SLF4J bindings."); Iterator iterator = implementationSet.iterator(); while(iterator.hasNext()) { URL path = (URL) iterator.next(); Util.report("Found binding in [" + path + "]"); } Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation."); } } catch (IOException ioe) { Util.report("Error getting resources from path", ioe); } }
2.綁定具體的日誌框架
根據找到的org/slf4j/impl/StaticLoggerBinder.class,執行StaticLoggerBinder.getSingleton()進行綁定。
private final static void bind() { try { // the next line does the binding StaticLoggerBinder.getSingleton();//綁定 INITIALIZATION_STATE = SUCCESSFUL_INITILIZATION; emitSubstituteLoggerWarning(); } catch (NoClassDefFoundError ncde) { String msg = ncde.getMessage(); if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) { INITIALIZATION_STATE = NOP_FALLBACK_INITILIZATION; Util .report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\"."); Util.report("Defaulting to no-operation (NOP) logger implementation"); Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details."); } else { failedBinding(ncde); throw ncde; } } catch(java.lang.NoSuchMethodError nsme) { String msg = nsme.getMessage(); if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) { INITIALIZATION_STATE = FAILED_INITILIZATION; 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. or 2.0.x"); } throw nsme; } catch (Exception e) { failedBinding(e); throw new IllegalStateException("Unexpected initialization failure", e); } }
因為具體的日誌框架為log4j,StaticLoggerBinder返回的為Log4j的LoggerFactory。
private final ILoggerFactory loggerFactory; ? private StaticLoggerBinder() { loggerFactory = new Log4jLoggerFactory(); try { Level level = Level.TRACE; } catch (NoSuchFieldError nsfe) { Util .report("This version of SLF4J requires log4j version 1.2.12 or later. See also http://www.slf4j.org/codes.html#log4j_version"); } }
經過以上步驟,slf4j將於具體的日誌框架log4j綁定。
如下所示,ILoggerFactory實際返回類型為Log4jLoggerFactory。
並調用Log4jLoggerFactory中的getLogger方法返回Log4jLogger用於log寫入。
slf4j日誌框架綁定機制