SLF4j:Log facade abstract
內 容:
- 應用中使用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接口。