標籤:

slf4j和Logback的對接

2015年閱讀Logback(1.1.3)源碼時,除了畫了一張UML圖,沒有做任何記錄。現在兩年過去了,基本什麼也記不清了。今天想到一個問題:slf4j和Logback的對接,只能重新追蹤一下源碼了。

Logger logger = LoggerFactory.getLogger(Test.class);

上面一行代碼是源碼的入口,從此進入一個函數調用鏈.

public static Logger getLogger(String name) { ILoggerFactory iLoggerFactory = getILoggerFactory(); return iLoggerFactory.getLogger(name); }

繼續深入,不難發現兩者的對接是在org.slf4j.LoggerFactory#bind中完成的(為了突出重點,忽略異常處理)。

private final static void bind() { Set staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); reportMultipleBindingAmbiguity(staticLoggerBinderPathSet); // the next line does the binding StaticLoggerBinder.getSingleton(); INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION; reportActualBinding(staticLoggerBinderPathSet); emitSubstituteLoggerWarning();}

注釋已經道破誰來綁定。那麼前面兩個方法有什麼作用呢?

private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";private static Set findPossibleStaticLoggerBinderPathSet() { // use Set instead of list in order to deal with bug #138 // LinkedHashSet appropriate here because it preserves insertion order during iteration Set staticLoggerBinderPathSet = new LinkedHashSet(); 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); } while (paths.hasMoreElements()) { URL path = (URL) paths.nextElement(); staticLoggerBinderPathSet.add(path); } } catch (IOException ioe) { Util.report("Error getting resources from path", ioe); } return staticLoggerBinderPathSet;}

上面的方法是搜索org/slf4j/impl/StaticLoggerBinder.class文件,把查到的文件放入一個集合里。注意:這個類不在sllf4j中,由日誌功能實現方提供!

reportMultipleBindingAmbiguity()看名字大概可以猜到其功能:Prints a warning message on the console if multiple bindings were found on the class path.

今天在公司本地環境下使用Logback,發現有一段紅色日誌:

SLF4J: Class path contains multiple SLF4J bindings.SLF4J: Found binding in [jar:file:/C:/Users/bjzhangxx/.m2/repository/ch/qos/logback/logback-classic/1.1.11/logback-classic-1.1.11.jar!/org/slf4j/impl/StaticLoggerBinder.class]SLF4J: Found binding in [jar:file:/C:/Users/bjzhangxx/.m2/repository/org/slf4j/slf4j-log4j12/1.7.25/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]

這段日誌表示發現了兩個org/slf4j/impl/StaticLoggerBinder.class,其中一個在logback中,另外一個在slf4j-log4j12.jar中,所以報警。正是這段日誌,引起我的思考,才寫出這篇文章。

順便說下,在logback中,返回的ILoggerFactory是ch.qos.logback.classic.LoggerContext,其返回的Logger本身就實現了org.slf4j.Logger。

而在slf4j-log4j12中會返回org.slf4j.impl.Log4jLoggerFactory,Log4jLoggerFactory中通過調用log4j的LogManager來創建log4j的Logger對象並通過Log4jLoggerAdapter類來包裝為slf4j的Logger(adapter模式)。

log4jLogger = LogManager.getLogger(name);Logger newInstance = new Log4jLoggerAdapter(log4jLogger);

假如程序只有隻有slf4j,沒有其他框架實現的StaticLoggerBinder,那麼在執行綁定哪一行會拋出異常,並列印以下錯誤信息:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".SLF4J: Defaulting to no-operation (NOP) logger implementationSLF4J: See SLF4J Error Codes for further details.

此時得到的Logger是 org.slf4j.helpers.NOPLogger,裡面的方法都是dummy

推薦閱讀:

聿申記2 夜夜夜夜
歐陸風雲4開發日誌 | 3/27 抓蟲子與下一個擴展包
希望世界和平(?)
歐陸風雲4開發日誌 | 4/3 新的政體機制與政府改革
#Deload Week -- 新年快樂

TAG:日誌 |