logback源碼分析-2.appender element

源碼基於logback 1.1.7java

logback.xml:app

<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://www.padual.com/java/logback.xsd">

    <appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>/Users/apple/Documents/UNKONESERVER/warn.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 按天回滾 daily -->
            <fileNamePattern>/Users/apple/Documents/UNKONESERVER/warn.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 日誌最大的保存天數 -->
            <maxHistory>2</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%msg%n</pattern>
        </encoder>
    </appender>

    <logger name="com.logback.test" level="debug"
            additivity="false">
        <appender-ref ref="WARN" />
    </logger>

    <root level="debug">
    </root>

</configuration>

下面分析loaback是如何解析appender的:oop

1.當解析到appender標籤的時候,觸發appender的startEvent,最終調用[configuration][appender]對應的AppenderAction 的begin方法:this

public void begin(InterpretationContext ec, String localName, Attributes attributes) throws ActionException {
        // We are just beginning, reset variables
        appender = null;
        inError = false;
        //獲取appender的類型,好比ch.qos.logback.core.rolling.RollingFileAppender
        String className = attributes.getValue(CLASS_ATTRIBUTE);
        if (OptionHelper.isEmpty(className)) {
            addError("Missing class name for appender. Near [" + localName + "] line " + getLineNumber(ec));
            inError = true;
            return;
        }

        try {
            addInfo("About to instantiate appender of type [" + className + "]");
            //根據appender的className初始化appender實例
            appender = (Appender<E>) OptionHelper.instantiateByClassName(className, ch.qos.logback.core.Appender.class, context);

            appender.setContext(context);
            //獲取appender標籤name屬性
            String appenderName = ec.subst(attributes.getValue(NAME_ATTRIBUTE));

            if (OptionHelper.isEmpty(appenderName)) {
                addWarn("No appender name given for appender of type " + className + "].");
            } else {
                //設置appender實例的name變量
                appender.setName(appenderName);
                addInfo("Naming appender as [" + appenderName + "]");
            }

            // The execution context contains a bag which contains the appenders
            // created thus far.
            //建立的appender實例後期都維護在ec的objectMap的appenderBag中
            HashMap<String, Appender<E>> appenderBag = (HashMap<String, Appender<E>>) ec.getObjectMap().get(ActionConst.APPENDER_BAG);

            // add the appender just created to the appender bag.
            appenderBag.put(appenderName, appender);
            //將appender實例推入ec的objectStack中
            ec.pushObject(appender);
        } catch (Exception oops) {
            inError = true;
            addError("Could not create an Appender of type [" + className + "].", oops);
            throw new ActionException(oops);
        }
    }

2.[configuration][appender]startEvent事後,即是[configuration][appender][file]的startEvent,可是[configuration][appender][file]沒有對應的action,根據以前的規則,file沒有內嵌元素,使用隱式action:NestedBasicPropertyIA。可是NestedBasicPropertyIA的begin爲空,即不作任何處理。debug

3.[configuration][appender][file]的startEvent事後,應該是[configuration][appender][file]的bodyEvent,最終會調用NestedBasicPropertyIA的body方法:日誌

public void body(InterpretationContext ec, String body) {
        //獲取file的body 即/Users/apple/Documents/UNKONESERVER/warn.log
        String finalBody = ec.subst(body);
        // get the action data object pushed in isApplicable() method call
        //actionData維護三個屬性:
        //1.parentBean:父級元素實例相關屬性,這裏file父級是appender實例
        //2.aggregationType:該element path的action類型,[configuration][appender][file]的action屬於AS_BASIC_PROPERTY類型
        //3.propertyName:做爲父級實例屬性的名稱或者可轉化成setXX形式的名稱,這裏是file,appender實例有一個叫file的屬性或者setFile的方法
        //這個action data是在查找對應的action時候,斷定映射的action爲隱式action時推入actionDataStack的
        IADataForBasicProperty actionData = (IADataForBasicProperty) actionDataStack.peek();
        switch (actionData.aggregationType) {
        case AS_BASIC_PROPERTY:
            //利用反射設置父級標籤實例的屬性值,這邊是經過調用appender實例的setFile方法設置了fileName
            actionData.parentBean.setProperty(actionData.propertyName, finalBody);
            break;
        case AS_BASIC_PROPERTY_COLLECTION:
            actionData.parentBean.addBasicProperty(actionData.propertyName, finalBody);
        default:
            addError("Unexpected aggregationType " + actionData.aggregationType);
        }
    }

4.[configuration][appender][file]的bodyEvent事後,就是[configuration][appender][file]的endEvent,最終會調用NestedBasicPropertyIA的end方法:code

public void end(InterpretationContext ec, String tagName) {
        // pop the action data object pushed in isApplicable() method call
        //上面的註釋很清楚,彈出在查找對應action時候推入的action data
        actionDataStack.pop();
    }

5.[configuration][appender][file]的endEvent事後,就是[configuration][appender][rollingPolicy]的startEvent。[configuration][appender][rollingPolicy]對應的action是NestedComplexPropertyIA,下面看NestedComplexPropertyIA的begin方法:component

public void begin(InterpretationContext ec, String localName, Attributes attributes) {
        // LogLog.debug("in NestComponentIA begin method");
        // get the action data object pushed in isApplicable() method call
        //actionData主要維護四個屬性:
        //1.parentBean:父級元素實例相關屬性,這裏rollingPolicy父級是appender實例
        //2.aggregationType:該element path的action類型,[configuration][appender][rollingPolicy]的action屬於AS_COMPLEX_PROPERTY類型
        //3.propertyName:做爲父級實例屬性的名稱,這裏是rollingPolicy,appender實例有一個叫rollingPolicy的屬性
        //4.nestedComplexProperty:根據class屬性實例化的對象,這裏是TimeBasedRollingPolicy實例對象
        IADataForComplexProperty actionData = (IADataForComplexProperty) actionDataStack.peek();
        //獲取class屬性值,這裏是ch.qos.logback.core.rolling.TimeBasedRollingPolicy
        String className = attributes.getValue(CLASS_ATTRIBUTE);
        // perform variable name substitution
        className = ec.subst(className);

        Class<?> componentClass = null;
        try {

            if (!OptionHelper.isEmpty(className)) {
                //實例化TimeBasedRollingPolicy
                componentClass = Loader.loadClass(className, context);
            } else {
                // guess class name via implicit rules
                PropertySetter parentBean = actionData.parentBean;
                componentClass = parentBean.getClassNameViaImplicitRules(actionData.getComplexPropertyName(), actionData.getAggregationType(),
                                ec.getDefaultNestedComponentRegistry());
            }

            if (componentClass == null) {
                actionData.inError = true;
                String errMsg = "Could not find an appropriate class for property [" + localName + "]";
                addError(errMsg);
                return;
            }

            if (OptionHelper.isEmpty(className)) {
                addInfo("Assuming default type [" + componentClass.getName() + "] for [" + localName + "] property");
            }
            //將TimeBasedRollingPolicy實例對象存入到actionData的nestedComplexProperty屬性裏
            actionData.setNestedComplexProperty(componentClass.newInstance());

            // pass along the repository
            if (actionData.getNestedComplexProperty() instanceof ContextAware) {
                ((ContextAware) actionData.getNestedComplexProperty()).setContext(this.context);
            }
            // addInfo("Pushing component [" + localName
            // + "] on top of the object stack.");
            //將TimeBasedRollingPolicy實例對象推入到ec的objectStack,
            //在推入以後:objectStack = {Stack@1600}  size = 3
            //              0 = {LoggerContext@1218} "ch.qos.logback.classic.LoggerContext[default]"
            //              1 = {RollingFileAppender@1266} "ch.qos.logback.core.rolling.RollingFileAppender[WARN]"
            //              2 = {TimeBasedRollingPolicy@1585} "c.q.l.core.rolling.TimeBasedRollingPolicy@436546048"
            ec.pushObject(actionData.getNestedComplexProperty());

        } catch (Exception oops) {
            actionData.inError = true;
            String msg = "Could not create component [" + localName + "] of type [" + className + "]";
            addError(msg, oops);
        }

    }

6.[configuration][appender][rollingPolicy]的startEvent事後,就是[configuration][appender][rollingPolicy][fileNamePattern]的startEvent,最終會調用NestedBasicPropertyIA的begin方法,此方法爲空。orm

7.[configuration][appender][rollingPolicy][fileNamePattern]的startEvent事後,就是[configuration][appender][rollingPolicy][fileNamePattern]的bodyEvent,最終會調用NestedBasicPropertyIA的body方法。根據前面的步驟3知道,NestedBasicPropertyIA的body方法做用主要是取出當前元素的值設置到父級元素的一個屬性中,當前元素名稱fileNamePattern,父級元素是rollingPolicy,利用反射調用父級元素的實例的setFileNamePattern,傳入fileNamePattern的值。xml

8.[configuration][appender][rollingPolicy][fileNamePattern]的bodyEvent事後,就是[configuration][appender][rollingPolicy][fileNamePattern]的endEvent,最終會調用NestedBasicPropertyIA的end方法,該方法只是彈出該element path對應的actionData。

9.接下來就是[configuration][appender][rollingPolicy][maxHistory],邏輯基本與fileNamePattern的同樣,讀者能夠自行debug看下。

10.[configuration][appender][rollingPolicy]的內嵌元素處理完以後就是觸發其endEvent方法了,最終會調用NestedComplexPropertyIA的end方法:

public void end(InterpretationContext ec, String tagName) {

        // pop the action data object pushed in isApplicable() method call
        // we assume that each this begin
        IADataForComplexProperty actionData = (IADataForComplexProperty) actionDataStack.pop();

        if (actionData.inError) {
            return;
        }
        //構建rollingPolicy的nestedBean
        PropertySetter nestedBean = new PropertySetter(beanDescriptionCache, actionData.getNestedComplexProperty());
        nestedBean.setContext(context);

        // have the nested element point to its parent if possible
        //若是該內嵌元素實例內部有屬性指向其父級元素,則須要該內嵌元素對應的實例的parent的屬性設置成父級元素的實例
        //這裏內嵌元素實例是TimeBasedRollingPolicy,設置TimeBasedRollingPolicy的parent值爲RollingFileAppender實例
        if (nestedBean.computeAggregationType("parent") == AggregationType.AS_COMPLEX_PROPERTY) {
            nestedBean.setComplexProperty("parent", actionData.parentBean.getObj());
        }

        // start the nested complex property if it implements LifeCycle and is not
        // marked with a @NoAutoStart annotation
        //從actionData中獲取TimeBasedRollingPolicy的實例,並調用其start方法
        Object nestedComplexProperty = actionData.getNestedComplexProperty();
        if (nestedComplexProperty instanceof LifeCycle && NoAutoStartUtil.notMarkedWithNoAutoStart(nestedComplexProperty)) {
            ((LifeCycle) nestedComplexProperty).start();
        }

        Object o = ec.peekObject();

        if (o != actionData.getNestedComplexProperty()) {
            addError("The object on the top the of the stack is not the component pushed earlier.");
        } else {
            ec.popObject();
            // Now let us attach the component
            switch (actionData.aggregationType) {
            case AS_COMPLEX_PROPERTY:
                //調用該元素的父級元素實例的set${TagName}方法將當前元素對應的實例存入到父級元素實例中
                //這裏是RollingFileAppender實例調用setRollingPolicy方法傳入TimeBasedRollingPolicy實例
                actionData.parentBean.setComplexProperty(tagName, actionData.getNestedComplexProperty());

                break;
            case AS_COMPLEX_PROPERTY_COLLECTION:
                actionData.parentBean.addComplexProperty(tagName, actionData.getNestedComplexProperty());
                break;
            default:
                addError("Unexpected aggregationType " + actionData.aggregationType);
            }
        }
    }

下面分析下TimeBasedRollingPolicy.start()方法:

public void start() {
        // set the LR for our utility object
        //renameUtil是爲了解決重命名文件時出現問題的通用類
        renameUtil.setContext(this.context);

        // find out period from the filename pattern
        //fileNamePatternStr的值爲以前解析fileNamePattern元素獲得的值
        //即/Users/apple/Documents/UNKONESERVER/warn.%d{yyyy-MM-dd}.log
        if (fileNamePatternStr != null) {
            //構建FileNamePattern對象,構建的時候主要作了如下幾件事:
            //1.將fileNamePatternStr中的\替換成/,並trim了下,存入到FileNamePattern的pattern屬性中
            //2.將)轉換成\),由於)是被認爲關鍵字,因此加了\
            //3.按照關鍵字,好比d、%轉換成一個個token
            fileNamePattern = new FileNamePattern(fileNamePatternStr, this.context);
            //根據日誌後綴決定用什麼壓縮方式,好比.gz等,正常是.log,表明不須要進行壓縮
            determineCompressionMode();
        } else {
            addWarn(FNP_NOT_SET);
            addWarn(CoreConstants.SEE_FNP_NOT_SET);
            throw new IllegalStateException(FNP_NOT_SET + CoreConstants.SEE_FNP_NOT_SET);
        }

        compressor = new Compressor(compressionMode);
        compressor.setContext(context);

        // wcs : without compression suffix
        fileNamePatternWCS = new FileNamePattern(Compressor.computeFileNameStr_WCS(fileNamePatternStr, compressionMode), this.context);

        addInfo("Will use the pattern " + fileNamePatternWCS + " for the active file");

        if (compressionMode == CompressionMode.ZIP) {
            String zipEntryFileNamePatternStr = transformFileNamePattern2ZipEntry(fileNamePatternStr);
            zipEntryFileNamePattern = new FileNamePattern(zipEntryFileNamePatternStr, context);
        }
        //若是沒有設置timeBasedFileNamingAndTriggeringPolicy的話使用默認的子策略,
        //好比設置下次歸檔時間,歸檔最大歷史時間,歸檔文件最大容量等
        if (timeBasedFileNamingAndTriggeringPolicy == null) {
            timeBasedFileNamingAndTriggeringPolicy = new DefaultTimeBasedFileNamingAndTriggeringPolicy<E>();
        }
        timeBasedFileNamingAndTriggeringPolicy.setContext(context);
        timeBasedFileNamingAndTriggeringPolicy.setTimeBasedRollingPolicy(this);
        timeBasedFileNamingAndTriggeringPolicy.start();

        if (!timeBasedFileNamingAndTriggeringPolicy.isStarted()) {
            addWarn("Subcomponent did not start. TimeBasedRollingPolicy will not start.");
            return;
        }

        // the maxHistory property is given to TimeBasedRollingPolicy instead of to
        // the TimeBasedFileNamingAndTriggeringPolicy. This makes it more convenient
        // for the user at the cost of inconsistency here.
        if (maxHistory != UNBOUND_HISTORY) {
            archiveRemover = timeBasedFileNamingAndTriggeringPolicy.getArchiveRemover();
            archiveRemover.setMaxHistory(maxHistory);
            archiveRemover.setTotalSizeCap(totalSizeCap.getSize());
            if (cleanHistoryOnStart) {
                addInfo("Cleaning on start up");
                Date now = new Date(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime());
                cleanUpFuture = archiveRemover.cleanAsynchronously(now);
            }
        } else if (totalSizeCap.getSize() != UNBOUND_TOTAL_SIZE) {
            addWarn("'maxHistory' is not set, ignoring 'totalSizeCap' option with value ["+totalSizeCap+"]");
        }

        super.start();
    }

11.[configuration][appender][rollingPolicy]結束後就是[configuration][appender][encoder],主要是設置日誌輸出格式。 12.最後就是[configuration][appender]的endEvent了,最終會調用AppenderAction的end方法:

/**
  * Once the children elements are also parsed, now is the time to activate the
  * appender options.
  */
//當子元素都被解析事後就該激活appender的end操做了
public void end(InterpretationContext ec, String name) {
        if (inError) {
            return;
        }

        if (appender instanceof LifeCycle) {
            //觸發appender的start方法:打開輸出日誌文件的outputStream,並設置到encoder中,後期日誌記錄委託給encoder處理
            ((LifeCycle) appender).start();
        }
        //先取出ec中objectStack的appender實例,判斷是否過早彈出後就真正的彈出了
        Object o = ec.peekObject();

        if (o != appender) {
            addWarn("The object at the of the stack is not the appender named [" + appender.getName() + "] pushed earlier.");
        } else {
            ec.popObject();
        }
    }
相關文章
相關標籤/搜索