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進行處理


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM