logback源码阅读-配置文件解析过程(六)

前面介绍了logback源码初始化过程是委托给ContextInitializer

StaticLoggerBinder

 void init() {
        try {
            try {
//<1> (
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); } this.contextSelectorBinder.init(this.defaultLoggerContext, KEY); this.initialized = true; } catch (Exception var3) { Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", var3); } }

ContextInitializer

<1>autoConfig

org.slf4j.impl.StaticLoggerBinder#init

->

ch.qos.logback.classic.util.ContextInitializer#autoConfig

public void autoConfig() throws JoranException {
        /**
         * <1>这里配置监听查找配置的消息
         * 判断系统变量是否有-Dlogback.statusListenerClass 参数
         * 等于SYSOUT 则默认使用OnConsoleStatusListener 这个是控制台打印
         * 否则当做配置类的全路径(我们自己创建一个StatusListener实现类)
* 可以实现对日志输出的监听 比如监听到之后分析 存入数据库
*/ StatusListenerConfigHelper.installIfAsked(this.loggerContext); /** *这里会依次查找 * <4>1.从系统变量查找配置文件-Dlogback.configurationFile={file} * 2.如果没有配置则依次找logback-test.xml logback.groovy logback.xml 找到任意一个返回 */ URL url = this.findURLOfDefaultConfigurationFile(true); if (url != null) { //<5>找到配置文件则走配置文件解析配置 this.configureByResource(url); } else { /** * 这里主要是java的SPI扩展点ServiceLoader 如果想实现自己的配置文件定义 可以通过这个做扩展 */ Configurator c = (Configurator) EnvUtil.loadFromServiceLoader(Configurator.class); if (c != null) { try { c.setContext(this.loggerContext); c.configure(this.loggerContext); } catch (Exception var4) { throw new LogbackException(String.format("Failed to initialize Configurator: %s using ServiceLoader", c != null ? c.getClass().getCanonicalName() : "null"), var4); } } else { //没有SPI扩展 则使用默认的配置 SPI扩展可以参考介个 BasicConfigurator basicConfigurator = new BasicConfigurator(); basicConfigurator.setContext(this.loggerContext); basicConfigurator.configure(this.loggerContext); } } }

<1>处主要是配置查找日志文件的监听器默认是控制台打印

 

 

 

 

 

 <2>处

<4>

ch.qos.logback.classic.util.ContextInitializer#findURLOfDefaultConfigurationFile

    public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {
        //获得当前对象的classLoader
        ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
        //先尝试从系统变量logback.configurationFile 找file配置文件
        URL url = this.findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
        if (url != null) {
            return url;
        } else {
            //没有配置先找logback-test.xml
            url = this.getResource("logback-test.xml", myClassLoader, updateStatus);
            if (url != null) {
                return url;
            } else {
                //再找logback.groovy
                url = this.getResource("logback.groovy", myClassLoader, updateStatus);
                return url != null ? url : this.getResource("logback.xml", myClassLoader, updateStatus);
            }
        }
    }

<5>

ch.qos.logback.classic.util.ContextInitializer#configureByResource

 public void configureByResource(URL url) throws JoranException {
        if (url == null) {
            throw new IllegalArgumentException("URL argument cannot be null");
        } else {
            String urlString = url.toString();
            //根据后缀判断是groovy配置还是xml配置 创建不同的解析器
            if (urlString.endsWith("groovy")) {
                if (EnvUtil.isGroovyAvailable()) {
                    GafferUtil.runGafferConfiguratorOn(this.loggerContext, this, url);
                } else {
                    StatusManager sm = this.loggerContext.getStatusManager();
                    sm.add(new ErrorStatus("Groovy classes are not available on the class path. ABORTING INITIALIZATION.", this.loggerContext));
                }
            } else {
                if (!urlString.endsWith("xml")) {
                    throw new LogbackException("Unexpected filename extension of file [" + url.toString() + "]. Should be either .groovy or .xml");
                }

                JoranConfigurator configurator = new JoranConfigurator();
                configurator.setContext(this.loggerContext);
                //<6>执行解析加载配置
                configurator.doConfigure(url);
            }

        }
    }

 

JoranConfigurator

类图

<6>

ch.qos.logback.core.joran.GenericConfigurator#doConfigure(java.net.URL)

 public final void doConfigure(URL url) throws JoranException {
        InputStream in = null;
        boolean var12 = false;

        String errMsg;
        try {
            var12 = true;
            //暂时也不知道干啥的
            informContextOfURLUsedForConfiguration(this.getContext(), url);
            //获得一个连接对象 这里是FileURLConnection 可以想象是否可以支持http呢 就可以远程配置文件了
            URLConnection urlConnection = url.openConnection();
            //不使用缓存
            urlConnection.setUseCaches(false);
            //获取流
            in = urlConnection.getInputStream();
            //<7>接下来看这个方法处理
            this.doConfigure(in, url.toExternalForm());
            var12 = false;
        } catch (IOException var15) {
            errMsg = "Could not open URL [" + url + "].";
            this.addError(errMsg, var15);
            throw new JoranException(errMsg, var15);
        } finally {
            if (var12) {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException var13) {
                        String errMsg = "Could not close input stream";
                        this.addError(errMsg, var13);
                        throw new JoranException(errMsg, var13);
                    }
                }

            }
        }

        if (in != null) {
            try {
                in.close();
            } catch (IOException var14) {
                errMsg = "Could not close input stream";
                this.addError(errMsg, var14);
                throw new JoranException(errMsg, var14);
            }
        }

    }

<7>

ch.qos.logback.core.joran.GenericConfigurator#doConfigure#doConfigure

   public final void doConfigure(InputSource inputSource) throws JoranException {
        long threshold = System.currentTimeMillis();
        SaxEventRecorder recorder = new SaxEventRecorder(this.context);
        //<8>这里利用Sax解析xml 并封装成SaxEvent
        recorder.recordEvents(inputSource);
        //<9>将封装成java对象的SaxEvent进行配置处理
        this.doConfigure(recorder.saxEventList);
        StatusUtil statusUtil = new StatusUtil(this.context);
        if (statusUtil.noXMLParsingErrorsOccurred(threshold)) {
            this.addInfo("Registering current configuration as safe fallback point");
            this.registerSafeConfiguration(recorder.saxEventList);
        }

    }

<1>

 

<8>

ch.qos.logback.core.joran.event.SaxEventRecorder#recordEvents

  public List<SaxEvent> recordEvents(InputSource inputSource) throws JoranException {
        SAXParser saxParser = this.buildSaxParser();

        try {
            //这里因为当前类继承了DefaultHandeler 所以通过这里将SAX解析成当前出席需要的对象
            saxParser.parse(inputSource, this);
            return this.saxEventList;
        } catch (IOException var4) {
            this.handleError("I/O error occurred while parsing xml file", var4);
        } catch (SAXException var5) {
            throw new JoranException("Problem parsing XML document. See previously reported errors.", var5);
        } catch (Exception var6) {
            this.handleError("Unexpected exception while parsing XML document.", var6);
        }
    }
    //解析开始标签
    public void startElement(String namespaceURI, String localName, String qName, Attributes atts) {
        String tagName = this.getTagName(localName, qName);
        this.globalElementPath.push(tagName);
        ElementPath current = this.globalElementPath.duplicate();
        this.saxEventList.add(new StartEvent(current, namespaceURI, localName, qName, atts, this.getLocator()));
    }
    //解析结束标签
    public void endElement(String namespaceURI, String localName, String qName) {
        this.saxEventList.add(new EndEvent(namespaceURI, localName, qName, this.getLocator()));
        this.globalElementPath.pop();
    }

<9>

ch.qos.logback.core.joran.GenericConfigurator#doConfigure(java.util.List<ch.qos.logback.core.joran.event.SaxEvent>)

doConfigure
 public void doConfigure(List<SaxEvent> eventList) throws JoranException {
        //<10>这里主要是初始化interpreter内部维护配置文件每个标签解析action的关系
        this.buildInterpreter();
        synchronized(this.context.getConfigurationLock()) {
            //开始映射对应的action进行解析
            this.interpreter.getEventPlayer().play(eventList);
        }
    }

<10>

ch.qos.logback.core.joran.GenericConfigurator#buildInterpreter

 protected void buildInterpreter() {
        RuleStore rs = new SimpleRuleStore(this.context);
        //<11>添加action映射关系 action为对应标签的解析器
        this.addInstanceRules(rs);
        this.interpreter = new Interpreter(this.context, rs, this.initialElementPath());
        InterpretationContext interpretationContext = this.interpreter.getInterpretationContext();
        interpretationContext.setContext(this.context);
        this.addImplicitRules(this.interpreter);
        this.addDefaultNestedComponentRegistryRules(interpretationContext.getDefaultNestedComponentRegistry());
    }

<11>

ch.qos.logback.classic.joran.JoranConfigurator#addInstanceRules

可以参考一下 实现自定义属性

 public void addInstanceRules(RuleStore rs) {
//<12>父类路由 比如Appender就在父类追加的 可以看到以下就是各个标签的Action解析器
super.addInstanceRules(rs); rs.addRule(new ElementSelector("configuration"), new ConfigurationAction()); rs.addRule(new ElementSelector("configuration/contextName"), new ContextNameAction()); rs.addRule(new ElementSelector("configuration/contextListener"), new LoggerContextListenerAction()); rs.addRule(new ElementSelector("configuration/insertFromJNDI"), new InsertFromJNDIAction()); rs.addRule(new ElementSelector("configuration/evaluator"), new EvaluatorAction()); rs.addRule(new ElementSelector("configuration/appender/sift"), new SiftAction()); rs.addRule(new ElementSelector("configuration/appender/sift/*"), new NOPAction()); rs.addRule(new ElementSelector("configuration/logger"), new LoggerAction()); rs.addRule(new ElementSelector("configuration/logger/level"), new LevelAction()); rs.addRule(new ElementSelector("configuration/root"), new RootLoggerAction()); rs.addRule(new ElementSelector("configuration/root/level"), new LevelAction()); rs.addRule(new ElementSelector("configuration/logger/appender-ref"), new AppenderRefAction()); rs.addRule(new ElementSelector("configuration/root/appender-ref"), new AppenderRefAction()); rs.addRule(new ElementSelector("*/if"), new IfAction()); rs.addRule(new ElementSelector("*/if/then"), new ThenAction()); rs.addRule(new ElementSelector("*/if/then/*"), new NOPAction()); rs.addRule(new ElementSelector("*/if/else"), new ElseAction()); rs.addRule(new ElementSelector("*/if/else/*"), new NOPAction()); if (PlatformInfo.hasJMXObjectName()) { rs.addRule(new ElementSelector("configuration/jmxConfigurator"), new JMXConfiguratorAction()); } rs.addRule(new ElementSelector("configuration/include"), new IncludeAction()); rs.addRule(new ElementSelector("configuration/consolePlugin"), new ConsolePluginAction()); rs.addRule(new ElementSelector("configuration/receiver"), new ReceiverAction()); }

 具体扩展可以参考一下appender实现 当我们需要自定义某个组件的时候也需要看一下对应action源码  比如appenderAction  实现了某个接口就调用start方法 内部可能给我们预留很多扩展

<12>

ch.qos.logback.core.joran.JoranConfiguratorBase#addInstanceRules

 protected void addInstanceRules(RuleStore rs) {
        rs.addRule(new ElementSelector("configuration/variable"), new PropertyAction());
        rs.addRule(new ElementSelector("configuration/property"), new PropertyAction());
        rs.addRule(new ElementSelector("configuration/substitutionProperty"), new PropertyAction());
        rs.addRule(new ElementSelector("configuration/timestamp"), new TimestampAction());
        rs.addRule(new ElementSelector("configuration/shutdownHook"), new ShutdownHookAction());
        rs.addRule(new ElementSelector("configuration/define"), new DefinePropertyAction());
        rs.addRule(new ElementSelector("configuration/contextProperty"), new ContextPropertyAction());
        rs.addRule(new ElementSelector("configuration/conversionRule"), new ConversionRuleAction());
        rs.addRule(new ElementSelector("configuration/statusListener"), new StatusListenerAction());
        rs.addRule(new ElementSelector("configuration/appender"), new AppenderAction());
        rs.addRule(new ElementSelector("configuration/appender/appender-ref"), new AppenderRefAction());
        rs.addRule(new ElementSelector("configuration/newRule"), new NewRuleAction());
        rs.addRule(new ElementSelector("*/param"), new ParamAction(this.getBeanDescriptionCache()));
    }

 

StatusListenerConfigHelper

<1>

ch.qos.logback.core.util.StatusListenerConfigHelper#installIfAsked

  public static void installIfAsked(Context context) {
        //从系统变量logback.statusListenerClass找到class
        String slClass = OptionHelper.getSystemProperty("logback.statusListenerClass");
        //如果有配置
        if (!OptionHelper.isEmpty(slClass)) {
            //<2>载入
            addStatusListener(context, slClass);
        }

    }

<2>

ch.qos.logback.core.util.StatusListenerConfigHelper#addStatusListener

  private static void addStatusListener(Context context, String listenerClassName) {
        StatusListener listener = null;
        //如果配置的是SYSOUT 则默认使用OnConsoleStatusListener
        if ("SYSOUT".equalsIgnoreCase(listenerClassName)) {
            listener = new OnConsoleStatusListener();
        } else {
            //<3>根据名字加载并初始化配置的Listener对象
            listener = createListenerPerClassName(context, listenerClassName);
        }

        initAndAddListener(context, (StatusListener)listener);
    }

<3>

ch.qos.logback.core.util.StatusListenerConfigHelper#initAndAddListener

private static StatusListener createListenerPerClassName(Context context, String listenerClass) {
        try {
            return (StatusListener)OptionHelper.instantiateByClassName(listenerClass, StatusListener.class, context);
        } catch (Exception var3) {
            var3.printStackTrace();
            return null;
        }
    }

 

总结

1.StaticLoggerBinder委托给ContextInitializer做初始化操作

2.首先会寻找-Dlogback.configurationFile={file} 是否有配置xml地址

3.没有找到则会依次在classpath下寻找logback-test.xml logback.groovy logback.xml  的配置文件 找到任意一个返回

4.如果没有找到会通过SPI扩展点看是否有自定义Config解析类

5.如果找到根据根据文件后缀走相应的解析类因为支持groovy格式文件 xml是走JoranConfigurator

6.JoranConfigurator会通过jdk SAX解析配置文件 然后自定义Handler将每个节点封装成SAXEvent

6.内部通过Interpreter 将维护的每个节点SAXEvent 所映射的Action进行处理

posted @ 2020-01-08 13:35  意犹未尽  阅读(1679)  评论(0编辑  收藏  举报