SLF4j:Log facade abstract,slf4jfacade
內 容:
- 應用中使用slf4j的工作流程
- 簡單樣本
- ILoggerFactory執行個體化過程
- 由ILoggerFactory建立Logger執行個體
- slf4j 適配器實現
- 自訂配接器
現如今,日誌架構層出不窮,JDKLogger、Log4j、Logback等這些是最常用的了。然而現在越來越多的架構中,都會在使用日誌架構的同時,還會使用到一個門面(slf4j-api.jar),使用這個門面的的最方便的地方大抵是它提供格式化字串的功能。
slf4j 與其他日誌架構的關係
在應用程式中,直接使用slf4j-api.jar就可以完成日誌的記錄,而不用在代碼裡直接使用某一種日誌架構了(雖然最終記錄日誌還是有日誌架構來完成的)。
下面是使用了slf4j時,應用程式、slf4j、slf4j-adapter.jar、日誌架構之間的調用關係:
下面是一個簡單的樣本:
package com.fjn.frame.slf4j;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class HelloWorld { public static void main(String[] args) { Logger logger = LoggerFactory.getLogger(HelloWorld.class); logger.info("Hello World"); }}
LoggerFactory.getLogger(xxx)要分為兩個過程:
1、執行個體化ILoggerFactory, 這個步驟只是在第一次進行。
2、根據ILoggerFactory執行個體建立Logger執行個體。
下面就分別來說說這兩個過程:
ILoggerFactory 執行個體化的過程
在使用slf4j時,並不需要指定具體使用哪種日誌架構,只需要給定相關的適配器包就可以了。那麼如何拿到真正的ILoggerFactory實現,並執行個體化的呢?
1、在LogFactory的classloader的搜尋路徑下尋找 ” org/slf4j/impl/StaticLoggerBinder.class” 類
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、將slf4j與指定的實現進行綁定,這一步的操作,通常是:
1)單一實例化StaticLoggerBinder。
2)檢查StaticLoggerBinder的版本,其實就是需要有一個靜態非final的變數(這個變數不是必須得有的),
StaticLoggerBinder.REQUESTED_API_VERSION
3、擷取到ILoggerFactory的執行個體
一般來說,是返回一個static ILoggerFactory impl=new XXXLoggerFactory();
由ILoggerFactory來建立Logger執行個體
LoggerFactory建立Logger執行個體,其實由日誌架構本身的LogManager建立一個Logger,然後封裝成LoggerAdapter。例如Log4j的適配包中的Log4jLoggerFactory#getLogger(String name)的實現如下:
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; } }
下面用是一張簡易的關係圖,顯示了適配器的實現:
自訂日誌架構適配器
在一些公司,肯定還有自己的Logger架構,如果也希望通過slf4j來做日誌,就需要寫相關的適配器包了。通過上述的兩個過程的瞭解,很容易就能知道如何自訂配接器了。
自訂配接器中,必須包括3個組件:
· StaticLoggerBinder
這個類需要遵守下列規約:
1) 類名必須是org.slf4j.impl.StaticLoggerBinder
2) 這個類必須是單例的,必須有getSingleton()方法
3) 儘可能的有 public static String REQUESTED_API_VERSION 欄位,並且不能是final的。
4) 要實現org.slf4j.spi.LoggerFactoryBinder介面。
· LoggerFactoryImpl
這個類要實現org.slf4j.ILoggerFactory介面
· LoggerAdapter
這個類要實現org.slf4j.Logger介面。