slf4j的含義為
Simple logging facade for Java
,其為簡單的為java實現的日志打印工具,本文則對其源碼進行簡單的分析
JAVA調用SLF4J
public class Test{
private static fianl Logger log = LoggerFactory.getLogger(Test.class) ;
public static void main(String[] args){
log.debug();
log.info();
log.error();
log.fatal();
}
}
注意調用slf4j接口用的是
LoggerFactory.getLogger()
方法,與log4j調用的LogManager.getLogger()
有點區別
Logger接口
內部屬性概覽
final public String ROOR_LOGGER_NAME = "ROOT" ;
public boolean isTraceEnabled();
public void trace(String msg);
....
....
簡單的看也就是定義了相應的級別輸出方法,比如trace()/info()/error()/debug()等
LoggerFactory內部屬性
其為final類型的class。羅列部分屬性,具體如下
//代表日志工具的初始化狀態
static final int UNINITIALIZED = 0;
static final int ONGOING_INITILIZATION = 1;
static final int FAILED_INITILIZATION = 2;
static final int SUCCESSFUL_INITILIZATION = 3;
static final int NOP_FALLBACK_INITILIZATION = 4;
//返回的均為NOPLogger類,即不打印任何日志信息
static SubstituteLoggerFactory TEMP_FACTORY = new SubstituteLoggerFactory();
static NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory();
LoggerFactory#getLogger()-獲取日志對象
其為靜態方法,源碼如下
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
繼續查看下LoggerFactory#getILoggerFactory()方法
LoggerFactory#getILoggerFactory()-獲取真實的日志工廠
源碼如下
public static ILoggerFactory getILoggerFactory() {
//初次獲取,初始化狀態為0
if (INITIALIZATION_STATE == UNINITIALIZED) {
//狀態置為1
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");
}
繼續觀察LoggerFactory#performInitialization()方法
LoggerFactory#performInitialization()-初始化日志操作
private final static void performInitialization() {
//檢查項目部署的環境即classpath下有無StaticLoggerBinder.class
singleImplementationSanityCheck();
//有則開始綁定
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITILIZATION) {
//對slf4j支持的版本進行確認
versionSanityCheck();
}
}
分別看下LoggerFactory#singleImplementationSanityCheck()方法和LoggerFactory#bind()方法
LoggerFactory#singleImplementationSanityCheck()-特定類存在判斷
特定類指的是org/slf4j/impl/StaticLoggerBinder.class
。具體源碼如下
private static void singleImplementationSanityCheck() {
try {
//獲取類加載器
ClassLoader loggerFactoryClassLoader = LoggerFactory.class
.getClassLoader();
//存放特定類的個數,當無相應的資源返回為空集合,但不為null
Enumeration paths;
if (loggerFactoryClassLoader == null) {
paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
} else {
paths = loggerFactoryClassLoader
.getResources(STATIC_LOGGER_BINDER_PATH);
}
List implementationList = new ArrayList();
//對獲取org/slf4j/impl/StaticLoggerBinder.class的資源進行遍歷
while (paths.hasMoreElements()) {
URL path = (URL) paths.nextElement();
implementationList.add(path);
}
//打印成功日志
if (implementationList.size() > 1) {
Util.report("Class path contains multiple SLF4J bindings.");
for (int i = 0; i < implementationList.size(); i++) {
Util.report("Found binding in [" + implementationList.get(i) + "]");
}
Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
}
} catch (IOException ioe) {
Util.report("Error getting resources from path", ioe);
}
}
主要任務是判斷是否存在
org/slf4j/impl/StaticLoggerBinder.class
,其在為bind()方法作預備調查,此方法目的是打印一些幫助信息。注意此處不建議擁有多個StaticLoggerBinder
類,一般要求只有單個StaticLoggerBinder
類存在,不然則會導致日志輸出不了
LoggerFactory#bind()-綁定獲取真實的日志處理類
bind()方法的處理邏輯顯得就很有意思了
private final static void bind() {
try {
//獲取StaticLoggerBinder單例
// the next line does the binding
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITILIZATION;
emitSubstituteLoggerWarning();
} catch (NoClassDefFoundError ncde) {
//對無此類的異常處理
String msg = ncde.getMessage();
//如果錯誤信息含有org/slf4j/impl/StaticLoggerBinder信息則設置初始化狀態為4
if (msg != null && msg.indexOf("org/slf4j/impl/StaticLoggerBinder") != -1) {
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 {
//狀態為2,並拋出異常
failedBinding(ncde);
throw ncde;
}
} catch(java.lang.NoSuchMethodError nsme) {
//對StaticLoggerBinder類無getSingleton()方法做處理
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);
}
}
bind()方法對
StaticLoggerBinder#getSingleton()
方法做了異常捕獲處理,處理邏輯如下:
對不存在StaticLoggerBinder類的
NoClassDefFoundError
異常,如果錯誤信息含有org/slf4j/impl/StaticLoggerBinder
信息則不拋出異常,但設置狀態為NOP_FALLBACK_INITILIZATION(4)
;反之則直接拋出異常,並設置狀態為FAILED_INITILIZATION(2)
對存在StaticLoggerBinder類但不存在getSingleton()方法的
NoSuchMethodError
異常,均拋出異常,並設置狀態為FAILED_INITILIZATION(2)
LoggerFactory#versionSanityCheck()-日志版本要求驗證
源碼如下
private final static void versionSanityCheck() {
try {
//此處為1.6
String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
//判斷是否與API的版本一致,此處為true
boolean match = false;
for (int i = 0; i < API_COMPATIBILITY_LIST.length; i++) {
if (requested.startsWith(API_COMPATIBILITY_LIST[i])) {
match = true;
}
}
if (!match) {
Util.report("The requested version " + requested
+ " by your slf4j binding is not compatible with "
+ Arrays.asList(API_COMPATIBILITY_LIST).toString());
Util.report("See " + VERSION_MISMATCH + " for further details.");
}
} catch (java.lang.NoSuchFieldError nsfe) {
// given our large user base and SLF4J's commitment to backward
// compatibility, we cannot cry here. Only for implementations
// which willingly declare a REQUESTED_API_VERSION field do we
// emit compatibility warnings.
} catch (Throwable e) {
// we should never reach here
Util.report("Unexpected problem occured during version sanity check", e);
}
}
目前的slf4j版本為1.6
小結
slf4j相當於是一個抽象接口,其會判斷classpath下是否存在
StaticLoggerBinder
類,並針對此類進行相應的邏輯處理,於此我們可以判斷出,其可以很好的被其他日志API接入,比如logback等
下節內容預告
針對返回狀態為
SUCCESSFUL_INITILIZATION
、NOP_FALLBACK_INITILIZATION
、FAILED_INITILIZATION
、ONGOING_INITILIZATION
時,創建的為何種ILoggerFactory,詳情見SLF4J源碼解析-LoggerFactory(二)