一:简介
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);
}
}