源码基于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(); } }