SLF4J的全稱是 Simple Logging Facade for Java(簡單java日志門面)
SLF4J自己不提供具體的日志功能實現,只是提供了一個統一的日志門面,在這個統一的門面之下,用戶可以選擇他們喜歡的日志的具體實現。
考慮下面一個場景:我們對外提供的工具包使用了log4j來管理日志,另外一個團隊如果使用logback來管理他們的日志,在引入我們提供的工具包之后他們的項目中就會有兩個不同的日志管理API,使用SLF4J可以解決這個問題。我們可以使用SLF4J + log4j來開發我們的工具包,客戶端在使用我們的API的時候可以不用引入log4j的包
我們在使用SLF4J的時候通常使用下面的方式來得到Logger
Logger logger = LoggerFactory.getLogger(ClassName); //Logger 和LoggerFactory都是 slf4j-api.jar中 org.slf4j包下邊的類
下邊是getLogger的定義:
public static Logger getLogger(String name) { ILoggerFactory iLoggerFactory = getILoggerFactory(); return iLoggerFactory.getLogger(name); }
getILoggerFactory()方法最終調用的是 LoggerFactory的bind()方法來確定具體是加載哪個日志的實現
private final static void bind() { try { Set<URL> staticLoggerBinderPathSet = null; // skip check under android, see also http://jira.qos.ch/browse/SLF4J-328 if (!isAndroid()) { staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); reportMultipleBindingAmbiguity(staticLoggerBinderPathSet); } // the next line does the binding StaticLoggerBinder.getSingleton(); INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION; reportActualBinding(staticLoggerBinderPathSet); replayEvents(); } catch (NoClassDefFoundError ncde) { String msg = ncde.getMessage(); if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) { INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION; 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.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) { INITIALIZATION_STATE = FAILED_INITIALIZATION; 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); } }
findPossibleStaticLoggerBinderPathSet()方法會在calsspath中尋找名為org.slf4j.impl.StaticLoggerBinder的類來確定項目中有哪些具體的日志實現
針對log4j來說,我們如果要使用SLF4J + log4j 來管理我們的日志,則需要三個相關的jar包(slf4j-api.jar、slf4j-log4j12.jar、log4j.jar)。slf4j-api.jar中有Logger、LoggerFactory等相應的類,slf4j-log4j12.jar中有org.slf4j.impl.StaticLoggerBinder 以及相應的 Log4jLoggerFactory 和能將log4j的Logger轉換為slf4j的Logger的Log4jLoggerAdapter(下圖是slf4j-log4j.jar中包含的類,一共6個:)
org.slf4j.impl.StaticLoggerBinder 類中綁定了Log4jLoggerFactory,在Log4jLoggerFactory的getLogger(String name)方法中使用 Log4jLoggerAdapter來實現了對log4j的注入:
public Logger getLogger(String name) { Logger slf4jLogger = loggerMap.get(name); if (slf4jLogger != null) { return slf4jLogger; } else { org.apache.log4j.Logger log4jLogger; if (name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME)) log4jLogger = LogManager.getRootLogger(); else log4jLogger = LogManager.getLogger(name); Logger newInstance = new Log4jLoggerAdapter(log4jLogger); Logger oldInstance = loggerMap.putIfAbsent(name, newInstance); return oldInstance == null ? newInstance : oldInstance; } }