ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

logger(二)、springBoot的日志源码查看(LogBack + slf4j)——创建ILoggerFactory

2021-06-22 11:04:23  阅读:216  来源: 互联网

标签:StaticLoggerBinder springBoot LoggerContext slf4j 源码 defaultLoggerContext return


上篇博客介绍了logback是怎么对接slf4j的,简言之,就是通过下面这行代码

slf4j委托具体实现框架的StaticLoggerBinder来返回一个ILoggerFactory,从而对接到具体实现框架上

这篇博客就接下来介绍一下,logback的StaticLoggerBinder类是怎么创建ILoggerFactory的

 

 

 

在图中可以看到,在logback里,ILoggerFactory的实现类是LoggerContext

logback的StaticLoggerBinder类实现了LoggerFactoryBinder接口,这个接口有两个方法

/**
 * An internal interface which helps the static {@link org.slf4j.LoggerFactory} 
 * class bind with the appropriate {@link ILoggerFactory} instance. 
 * 
 * @author Ceki Gülcü
 */
public interface LoggerFactoryBinder {

    /**
     * Return the instance of {@link ILoggerFactory} that 
     * {@link org.slf4j.LoggerFactory} class should bind to.
     * 
     * @return the instance of {@link ILoggerFactory} that 
     * {@link org.slf4j.LoggerFactory} class should bind to.
     */
    public ILoggerFactory getLoggerFactory();

    /**
     * The String form of the {@link ILoggerFactory} object that this 
     * <code>LoggerFactoryBinder</code> instance is <em>intended</em> to return. 
     * 
     * <p>This method allows the developer to interrogate this binder's intention
     * which may be different from the {@link ILoggerFactory} instance it is able to 
     * yield in practice. The discrepancy should only occur in case of errors.
     * 
     * @return the class name of the intended {@link ILoggerFactory} instance
     */
    public String getLoggerFactoryClassStr();
}

其中比较重要的是getLoggerFactory()方法,其实自定义的StaticLoggerBinder类不实现这个接口也是可以的,只要能保证提供getLoggerFactory()方法,并返回一个ILoggerFactory就可以了

下面就来具体地看看StaticLoggerBinder类的代码:

首先,该类必须有一个getSingleton()方法,来返回该类的单例

public class StaticLoggerBinder implements LoggerFactoryBinder {
    public static String REQUESTED_API_VERSION = "1.7.16";
    static final String NULL_CS_URL = "http://logback.qos.ch/codes.html#null_CS";
    private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
    private static Object KEY = new Object();
    private boolean initialized = false;
    private LoggerContext defaultLoggerContext = new LoggerContext();
    private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder.getSingleton();

    private StaticLoggerBinder() {
        this.defaultLoggerContext.setName("default");
    }

    public static StaticLoggerBinder getSingleton() {
        return SINGLETON;
    }

以上代码用了比较简单的单例模式,提供getSingleton()方法是对接slf4j的强制要求

然后这个类用了一个static块来保证初始化

    void init() {
        try {
            try {
                //委托ContextInitializer类对defaultLoggerContext进行初始化
                (new ContextInitializer(this.defaultLoggerContext)).autoConfig();
            } catch (JoranException var2) {
                Util.report("Failed to auto configure default logger context", var2);
            }

            if (!StatusUtil.contextHasStatusListener(this.defaultLoggerContext)) {
                StatusPrinter.printInCaseOfErrorsOrWarnings(this.defaultLoggerContext);
            }
            //对ContextSelectorStaticBinder类进行初始化
            this.contextSelectorBinder.init(this.defaultLoggerContext, KEY);
            this.initialized = true;
        } catch (Exception var3) {
            Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", var3);
        }

    }

    static {
        SINGLETON.init();
    }

 

这个初始化方法init()里做了2件事:

第一件事是委托ContextInitializer类对defaultLoggerContext进行初始化。这里如果找到了任一配置文件,就会根据配置文件去初始化LoggerContext,如果没找到,会使用默认配置。关于LoggerContext是怎么根据配置文件进行配置的,在后面的博客中介绍,这里先略过

第二件事是对ContextSelectorStaticBinder类进行初始化

    public void init(LoggerContext defaultLoggerContext, Object key) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        if (this.key == null) {
            this.key = key;
        } else if (this.key != key) {
            throw new IllegalAccessException("Only certain classes can access this method.");
        }

        String contextSelectorStr = OptionHelper.getSystemProperty("logback.ContextSelector");
        if (contextSelectorStr == null) {
            this.contextSelector = new DefaultContextSelector(defaultLoggerContext);
        } else if (contextSelectorStr.equals("JNDI")) {
            this.contextSelector = new ContextJNDISelector(defaultLoggerContext);
        } else {
            this.contextSelector = dynamicalContextSelector(defaultLoggerContext, contextSelectorStr);
        }

    }

如果系统参数中配置了JNDI,这里会得到一个ContextJNDISelector,实际应用中,一般会得到一个DefaultContextSelector,并且把已经初始化完成的defaultLoggerContext传给新创建的这个DefaultContextSelector

经过上面的步骤,StaticLoggerBinder的init()方法就走完了,接下来就会调用到关键的getLoggerFactory()方法

    public ILoggerFactory getLoggerFactory() {
        if (!this.initialized) {
            return this.defaultLoggerContext;
        } else if (this.contextSelectorBinder.getContextSelector() == null) {
            throw new IllegalStateException("contextSelector cannot be null. See also http://logback.qos.ch/codes.html#null_CS");
        } else {
            return this.contextSelectorBinder.getContextSelector().getLoggerContext();
        }
    }

可以看到,这里有2条分支,如果initialized是false,那么会直接返回defaultLoggerContext。否则就委托刚才提到的ContextSelectorStaticBinder返回一个ContextSelector(一般就是DefaultContextSelector),然后由ContextSelector来返回LoggerContext

 

public class DefaultContextSelector implements ContextSelector {
    private LoggerContext defaultLoggerContext;

    public DefaultContextSelector(LoggerContext context) {
        this.defaultLoggerContext = context;
    }

    public LoggerContext getLoggerContext() {
        return this.getDefaultLoggerContext();
    }

    public LoggerContext getDefaultLoggerContext() {
        return this.defaultLoggerContext;
    }

    public LoggerContext detachLoggerContext(String loggerContextName) {
        return this.defaultLoggerContext;
    }

    public List<String> getContextNames() {
        return Arrays.asList(this.defaultLoggerContext.getName());
    }

    public LoggerContext getLoggerContext(String name) {
        return this.defaultLoggerContext.getName().equals(name) ? this.defaultLoggerContext : null;
    }
}

总结一下这个过程:
1、StaticLoggerBinder在加载的时候,会去读取配置文件,并根据配置文件对LoggerContext进行初始化
2、然后初始化ContextSelectorStaticBinder,在这个类内部new一个DefaultContextSelector,并把第一步中配置完毕的LoggerContext传给DefaultContextSelector
3、调用getLoggerFactory()方法,直接返回第一步中配置的LoggerContext,或者委托DefaultContextSelector类返回LoggerContext

 

博客来源:

由于使用的版本较高,本文稍有不同,新版本中也见到很多新的编程思想

读logback源码系列文章(二)——提供ILoggerFactory

标签:StaticLoggerBinder,springBoot,LoggerContext,slf4j,源码,defaultLoggerContext,return
来源: https://www.cnblogs.com/lzghyh/p/14904904.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有