源碼基於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(); } }