栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

看源码,搞明白Logback是如何自动生效的?

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

看源码,搞明白Logback是如何自动生效的?

导语:

private static final Logger logger = LoggerFactory.getLogger(ClassName.class);对于使用Springboot的Java开发的人员来说,当你使用Slf4j+Logback的日志框架时,这行代码一定不陌生。细心的人会发现,Logger类和LoggerFactory类都是slf4j的,那么Logback有什么用呢?我也有这个疑问,所以我抽空看了一下源码,发现了他是如何使用Logback的。第一次写源码相关的文章,有不对的地方欢迎批评指正。

Slf4j和Logback的关系

通过百度可以知道。。。

Slf4j是一种日志框架接口设计,是没有具体的业务实现的,想要使用Slf4j记录日志:1、自己实现Slf4j相关接口;2、直接使用实现了Slf4j的相关日志框架,如Log4j何Logback。Logback是Slf4j原生实现的日志框架。由于前段时间Log4j接连被爆出多个高危漏洞,让使用Logback的人变多了。 确定是否使用了Logback相关的类

首先,我们要确定记录日志时候,是否使用了Logback相关的实现类。

    找到项目中任意一个LoggerFactory.getLogger(ClassName.class);这样的代码,点进getLogger();方法内部,会发现只有两行代码,我们在return那里打个断点(代码中用【断点】表示该行打了断点)。
    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(),看到如下代码,刚看到可能有点蒙蔽,不要怕,咱们就在第一行打断点,看看他到底怎么执行的就完事了。

    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");
    }
    通过第3不得断点,单步执行,我们会发现如下两件事:

    第一个if进去了(应该是系统首次启动运行才会进入,至于为啥往下看你应该能明白)在switch--case中,进入了第一个case并成功返回

后边我们重点要搞明白这两点。 深入分析

经过以上4步的初步看源码,我们发现了两件事,简称4-1和4-2。
之所以在这令起一个标题叫深入分析,是因为经过分析这两件事,就可以搞明白【Slf4j是如何引用Logback的类的?】这个问题

INITIALIZATION_STATE

从字面意思,咱们能猜到这个变量的含义是【初始化状态】,可以看做一个状态码
首先,咱们看看INITIALIZATION_STATE值得变化过程

初始值:INITIALIZATION_STATE就是UNINITIALIZED,声明时就初始化,表示【未初始化】

static volatile int INITIALIZATION_STATE = UNINITIALIZED;

正在初始化:仔细看上边看第3步贴出的代码中,if中有一行代码如下

INITIALIZATION_STATE = ONGOING_INITIALIZATION;

初始化成功:仍然是第3步的代码,我们看到INITIALIZATION_STATE的值,在到switch--case中时,已经变成了SUCCESSFUL_INITIALIZATION,所以才进入第一个case。那么为什么会变成SUCCESSFUL_INITIALIZATION就是需要继续深入探究的事情。 bind()

还是回到第3步,有一个方法是performInitialization(),之所以我们这个标题叫做bind(),是因为在performInitialization()方法中,最主要的就是bind()。

两个方法内部代码如下:

performInitialization()

    private final static void performInitialization() {
        bind();
        if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
            versionSanityCheck();
        }
    }

bind()

    private final static void bind() {
        try {
            Set 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()两个方法内部的代码,已经是晕了,不要慌,咱们慢慢捋,就算他是弹簧,咱也要把它捋直了。

接下来就不那么多废话了,提高下效率,按顺序来

findPossibleStaticLoggerBinderPathSet()

先进入方法内部打断点,看到如下信息,重要:

总结1

根据上面的两个截图,经过了一些不可描述的过程(由于我的水平有限,暂时理解不了),虽然我不知道细节和原理,但是在整体上我有一点点理解,通俗的总结以下两点:

    先找到Slf4j的加载器找到本地使用的,实现了Slf4j接口的,jar包目录

reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);这行代码咱们就不看了,经过断点发现,该方法内部的逻辑就没走(没进到内部的if中)

重点:StaticLoggerBinder.getSingleton();

bind()方法内部,执行了StaticLoggerBinder.getSingleton();该方法,由于这块一些代码互相调用,且都是写在一起的,我就一起把这一套代码贴出来

    
    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;
    }
总结2

看到上班getSingleton()中直接返回了一个类实例,而这个类好巧不巧,就是findPossibleStaticLoggerBinderPathSet()方法中第一个截图时的那个类:

这时候,仔细看你会发现,这时已经进入Logback内部了,如果不仔细,只看包路径,你还以为仍然在Slf4j体(包)内乱撞那,等你回过神来,熟不知已经进入了贤者模式。。。。

猜想

水平有限,且没有过于深究,所以不敢叫结论,只敢叫猜想,如果猜想有错,甚至完全错误,请您狠狠地纠正我!

    Slf4j包有他自己写好的类实例化逻辑Slf4j可能已经写死了一些信息,比如他的实现类的包路径和类名,如org.slf4j.impl.StaticLoggerBinder一个项目同时存在多个Slf4j的org.slf4j.impl.StaticLoggerBinder实现类,会发生冲突,冲突原因可能是:Slf4j不知道该让谁在他体(包)内乱撞。。。。

其中第三点猜想经过百度大概率证实了,因为好像项目中同时引用Log4j和Logback会发生冲突,而这两个日志工具都是基于Slf4j去实现的。

OK,结束!

本文还是有很多地方没有真正搞清楚,想搞清楚的大佬可以自行去看源码学习了解。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/785620.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号