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

Posted lqblog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了logback源码阅读-配置文件解析过程相关的知识,希望对你有一定的参考价值。

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

StaticLoggerBinder

 void init() {
        try {
            try {
                (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

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);
        /**
         *这里会依次查找
         * <2>1.从系统变量查找配置文件-Dlogback.configurationFile={file}
         *    2.如果没有配置则依次找logback-test.xml logback.groovy logback.xml  找到任意一个返回
         */
        URL url = this.findURLOfDefaultConfigurationFile(true);
        if (url != null) {
            //找到配置文件则走配置文件解析配置
            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>处

技术图片

configureByResource

org.slf4j.impl.StaticLoggerBinder#init

->

ch.qos.logback.classic.util.ContextInitializer#autoConfig#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结尾 按照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");
                }
                //如果是xml结尾则走xml的解析方式 委托给了JoranConfigurator
                JoranConfigurator configurator = new JoranConfigurator();
                configurator.setContext(this.loggerContext);
                configurator.doConfigure(url);
            }

        }
    }

JoranConfigurator

类图

技术图片

doConfigure

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();
            //接下来看这个方法处理
            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);
            }
        }

    }

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);
        //<1>这里利用Sax解析xml 并封装成SaxEvent
        recorder.recordEvents(inputSource);
        //<2>将封装成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>技术图片

 

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();
    }

 <2>处代码

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

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

 protected void buildInterpreter() {
        RuleStore rs = new SimpleRuleStore(this.context);
        //添加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());
    }

ch.qos.logback.classic.joran.JoranConfigurator

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

 public void addInstanceRules(RuleStore rs) {
//父类路由 比如Appender就在父类追加的
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方法 内部可能给我们预留很多扩展

 

以上是关于logback源码阅读-配置文件解析过程的主要内容,如果未能解决你的问题,请参考以下文章

logback源码阅读-Encoder

日志 Logback 配置文件这么写,TPS 提高 10 倍

logback源码阅读-Appender

阅读源码很重要,以logback为例,分享一个小白都能学会的读源码方法

Nginx 模块自主开发六:源码剖析配置文件解析过程

从源码来理解slf4j的绑定,以及logback对配置文件的加载