一:簡介
1.因爲最近在搭建一個項目,需要用到ELK框架+filebeat,來整理、分析和可視化日誌,所以就花些時間一併消化整理了;
2.slf4j的官網文檔的解釋:它是一系列日誌框架的簡單的外觀或者抽象,比如logback和log4j;它允許使用者在部署時去插入需要的日誌框架;而且你的應用啓用SLF4J,只需要一個額外的依賴:slf4j-api-xxxx.jar。
The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks, such as java.util.logging, logback and log4j. SLF4J allows the end-user to plug in the desired logging framework at deployment time. Note that SLF4J-enabling your library/application implies the addition of only a single mandatory dependency, namely slf4j-api-1.7.21.jar.
3.加載失敗的日誌,下文是沒有找到對應配置的例子,會直接使用logback.classic的默認配置:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/xxxx/.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:/Users/xxxx/.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]
4.slf4加載成功的日誌:下文就是正確加載logback的例子,在其路徑下配置了logback-test.xml之後,slf4j會找到對應的配置文件,並加載配置;
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/xxxx/.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:/Users/xxxx/.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.17:23:15,953 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
17:23:15,953 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback-test.xml] at [file:/Users/xxxx/xxxx/target/classes/logback-test.xml]
17:23:16,075 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
17:23:16,085 |-INFO in ch.qos.logback.core.joran.util.ConfigurationWatchListUtil@1df8b5b8 - Adding [jar:file:/Users/xxxx/.m2/repository/org/springframework/boot/spring-boot/1.5.9.RELEASE/spring-boot-1.5.9.RELEASE.jar!/org/springframework/boot/logging/logback/defaults.xml] to configuration watch list.
17:23:16,085 |-INFO in ch.qos.logback.core.joran.spi.ConfigurationWatchList@23202fce - URL [jar:file:/Users/xxxx/.m2/repository/org/springframework/boot/spring-boot/1.5.9.RELEASE/spring-boot-1.5.9.RELEASE.jar!/org/springframework/boot/logging/logback/defaults.xml] is not of type file
二:配置生效過程
1.使用方法,一般的獲取Logger是用下面的方式:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final Logger logger = LoggerFactory.getLogger(getClass());
2.在應用程序啓動中,會去調用上面的方法,這個時候Slf4j就會去搜索對應的配置,並加載,詳情見下面LoggerFactory的實現:
public static Logger getLogger(Class<?> clazz) {
Logger logger = getLogger(clazz.getName());
if (DETECT_LOGGER_NAME_MISMATCH) {
Class<?> autoComputedCallingClass = Util.getCallingClass();
if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), autoComputedCallingClass.getName()));
Util.report("See http://www.slf4j.org/codes.html#loggerNameMismatch for an explanation");
}
}
return logger;
}
public static Logger getLogger(String name) {
/*獲取到對應的Logger工廠類*/
ILoggerFactory iLoggerFactory = getILoggerFactory();
/*利用實際的工廠類,加載logger*/
return iLoggerFactory.getLogger(name);
}
public static ILoggerFactory getILoggerFactory() {
/*保證只初始化一次*/
if (INITIALIZATION_STATE == 0) {
Class var0 = LoggerFactory.class;
synchronized(LoggerFactory.class) {
if (INITIALIZATION_STATE == 0) {
/*Double check*/
INITIALIZATION_STATE = 1;
performInitialization();
}
}
}
switch(INITIALIZATION_STATE) {
/*如果沒有綁定,則返回默認工廠*/
case 1:
return SUBST_FACTORY;
case 2:
/*異常*/
throw new IllegalStateException("org.slf4j.LoggerFactory in failed state. Original exception was thrown EARLIER. See also http://www.slf4j.org/codes.html#unsuccessfulInit");
/*找到合適的Logger實現類*/
case 3:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
case 4:
/*沒有返回*/
return NOP_FALLBACK_FACTORY;
/*異常*/
default:
throw new IllegalStateException("Unreachable code");
}
/*執行初始化*/
private static final void performInitialization() {
/*先去尋找可綁定的Logger實現*/
bind();
if (INITIALIZATION_STATE == 3) {
versionSanityCheck();
}
}
3.在bind函數中,Slf4j會去搜索,並綁定Logger實現;
private static final void bind() {
String msg;
try {
Set<URL> staticLoggerBinderPathSet = null;
/*如果不是Android,則去尋找可用的Logger綁定路徑*/
if (!isAndroid()) {
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
/*如果有多個可用的,去打印*/
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
/*設置默認的Logger實現類*/
StaticLoggerBinder.getSingleton();
/*表示找到合適的Logger實現類*/
INITIALIZATION_STATE = 3;
/*打印實際使用的Logger,並綁定合適的Logger*/
reportActualBinding(staticLoggerBinderPathSet);
fixSubstituteLoggers();
replayEvents();
SUBST_FACTORY.clear();
} catch (NoClassDefFoundError var2) {
msg = var2.getMessage();
if (!messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
failedBinding(var2);
throw var2;
}
INITIALIZATION_STATE = 4;
Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
Util.report("Defaulting to no-operation (NOP) logger implementation");
Util.report("See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.");
} catch (NoSuchMethodError var3) {
msg = var3.getMessage();
if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
INITIALIZATION_STATE = 2;
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 var3;
} catch (Exception var4) {
failedBinding(var4);
throw new IllegalStateException("Unexpected initialization failure", var4);
}
}