zoukankan      html  css  js  c++  java
  • 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进行处理

  • 相关阅读:
    JQuery EasyUi之界面设计——通用的JavaScript
    easyui datagrid 行右键 动态获取并生成toolbar 按钮
    Jq基础拓展 json to String
    电信光纤猫(HG8245)破解教程 开启无线网、路由器功能(第二章)
    plsql 无需配置客户端连接.
    中文分词常用算法之基于词典的正向最大匹配
    中文分词常用算法之基于词典的逆向最大匹配
    SQL SERVER安装序列号
    查询锁事务及语句
    SQL Server 数据库备份
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12166127.html
Copyright © 2011-2022 走看看