private static final Logger logger = LoggerFactory.getLogger(ClassName.class);
Springboot
的Java
开发的人员来说,当你使用Slf4j
+Logback
的日志框架时,这行代码一定不陌生。细心的人会发现,Logger
类和LoggerFactory
类都是slf4j
的,那么Logback
有什么用呢?我也有这个疑问,所以我抽空看了一下源码,发现了他是如何使用Logback
的。通过百度可以知道。。。
Slf4j
是一种日志框架接口设计,是没有具体的业务实现的,想要使用Slf4j
记录日志:1、自己实现Slf4j
相关接口;2、直接使用实现了Slf4j
的相关日志框架,如Log4j
何Logback
。Logback
是Slf4j
原生实现的日志框架。由于前段时间Log4j
接连被爆出多个高危漏洞,让使用Logback
的人变多了。首先,我们要确定记录日志时候,是否使用了Logback相关的实现类。
LoggerFactory.getLogger(ClassName.class);
这样的代码,点进 getLogger();
方法内部,会发现只有两行代码,我们在return
那里打个断点(代码中用【断点】
表示该行打了断点)。java public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
【断点】return iLoggerFactory.getLogger(name);
}
ILoggerFactory
类,他已经是Logback
中的一个实现类ch.qos.logback.classic.LoggerContext
,如下图:Logback
的相关实现,但是不是说Logback
是Slf4j
的实现类吗,那么Slf4j
是如何引用Logback
的类的?下面继续一探究竟。接下来点进第1步两行代码中,getILoggerFactory()
,看到如下代码,刚看到可能有点蒙蔽,不要怕,咱们就在第一行打断点,看看他到底怎么执行的就完事了。java public static ILoggerFactory getILoggerFactory() {
【断点】if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
performInitialization();
}
}
}
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
case NOP_FALLBACK_INITIALIZATION:
return NOP_FALLBACK_FACTORY;
case FAILED_INITIALIZATION:
throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
case ONGOING_INITIALIZATION:
// support re-entrant behavior.
// See also http://jira.qos.ch/browse/SLF4J-97
return SUBST_FACTORY;
}
throw new IllegalStateException("Unreachable code");
}
if
进去了(应该是系统首次启动运行才会进入,至于为啥往下看你应该能明白)switch--case
中,进入了第一个case
并成功返回经过以上4步的初步看源码,我们发现了两件事,简称
4-1
和4-2
。 之所以在这令起一个标题叫深入分析
,是因为经过分析这两件事,就可以搞明白【Slf4j
是如何引用Logback
的类的?】这个问题
从字面意思,咱们能猜到这个变量的含义是【初始化状态】,可以看做一个状态码 首先,咱们看看
INITIALIZATION_STATE
值得变化过程
INITIALIZATION_STATE
就是UNINITIALIZED
,声明时就初始化,表示【未初始化】javastatic volatile int INITIALIZATION_STATE = UNINITIALIZED;
if
中有一行代码如下javaINITIALIZATION_STATE = ONGOING_INITIALIZATION;
INITIALIZATION_STATE
的值,在到switch--case
中时,已经变成了SUCCESSFUL_INITIALIZATION
,所以才进入第一个case
。那么为什么会变成SUCCESSFUL_INITIALIZATION
就是需要继续深入探究的事情。还是回到第3步,有一个方法是
performInitialization()
,之所以我们这个标题叫做bind()
,是因为在performInitialization()
方法中,最主要的就是bind()
。
两个方法内部代码如下:
java private final static void performInitialization() {
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
versionSanityCheck();
}
}
java private final static void bind() {
try {
Set<URL> staticLoggerBinderPathSet = null;
// skip check under android, see also
// http://jira.qos.ch/browse/SLF4J-328
if (!isAndroid()) {
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// the next line does the binding
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(staticLoggerBinderPathSet);
} catch (NoClassDefFoundError ncde) {
String msg = ncde.getMessage();
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
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 {
failedBinding(ncde);
throw ncde;
}
} catch (java.lang.NoSuchMethodError nsme) {
String msg = nsme.getMessage();
if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
INITIALIZATION_STATE = FAILED_INITIALIZATION;
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 nsme;
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
} finally {
postBindCleanUp();
}
}
ok,一般情况下,看到
bind()
里的代码,尤其是findPossibleStaticLoggerBinderPathSet()
、reportMultipleBindingAmbiguity()
两个方法内部的代码,已经是晕了,不要慌,咱们慢慢捋,就算他是弹簧,咱也要把它捋直了。
接下来就不那么多废话了,提高下效率,按顺序来
先进入方法内部打断点,看到如下信息,重要:
根据上面的两个截图,经过了一些不可描述的过程(由于我的水平有限,暂时理解不了),虽然我不知道细节和原理,但是在整体上我有一点点理解,通俗的总结以下两点:
Slf4j
的加载器Slf4j
接口的,jar包目录reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
这行代码咱们就不看了,经过断点发现,该方法内部的逻辑就没走(没进到内部的if
中)
bind()
方法内部,执行了StaticLoggerBinder.getSingleton();
该方法,由于这块一些代码互相调用,且都是写在一起的,我就一起把这一套代码贴出来
java /**
* The unique instance of this class.
*/
private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
private static Object KEY = new Object();
static {
SINGLETON.init();
}
private boolean initialized = false;
private LoggerContext defaultLoggerContext = new LoggerContext();
private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder.getSingleton();
private StaticLoggerBinder() {
defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);
}
public static StaticLoggerBinder getSingleton() {
return SINGLETON;
}
看到上班getSingleton()
中直接返回了一个类实例,而这个类好巧不巧,就是findPossibleStaticLoggerBinderPathSet()
方法中第一个截图时的那个类:
这时候,仔细看你会发现,这时已经进入Logback
内部了,如果不仔细,只看包路径,你还以为仍然在Slf4j
体(包)内乱撞那,等你回过神来,熟不知已经进入了贤者模式。。。。
水平有限,且没有过于深究,所以不敢叫结论,只敢叫猜想,如果猜想有错,甚至完全错误,请您狠狠地纠正我!
Slf4j
包有他自己写好的类实例化逻辑Slf4j
可能已经写死了一些信息,比如他的实现类的包路径和类名,如org.slf4j.impl.StaticLoggerBinder
Slf4j
的org.slf4j.impl.StaticLoggerBinder
实现类,会发生冲突,冲突原因可能是:Slf4j
不知道该让谁在他体(包)内乱撞。。。。其中第三点猜想经过百度大概率证实了,因为好像项目中同时引用Log4j
和Logback
会发生冲突,而这两个日志工具都是基于Slf4j
去实现的。
OK,结束!
本文还是有很多地方没有真正搞清楚,想搞清楚的大佬可以自行去看源码学习了解。
建站因为热爱,生活需要Money,请屏幕前的大佬动动您发财的小手,点击一次以示鼓励,祝您生活愉快!
PS:就目前的访问量,即便每个访客都点一次广告,收入也不足以支付运营成本。
如果看不到广告,可能是网络原因或被拦截了,那就算了吧。再次祝您生活愉快~~
本文作者:DingDangDog
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!