源码基于logback 1.1.7
logback.xml:
<?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的:
1.当解析到appender标签的时候,触发appender的startEvent,最终调用[configuration][appender]对应的AppenderAction 的begin方法:
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为空,即不做任何处理。
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方法:
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方法:
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方法,此方法为空。
7.[configuration][appender][rollingPolicy][fileNamePattern]的startEvent过后,就是[configuration][appender][rollingPolicy][fileNamePattern]的bodyEvent,最终会调用NestedBasicPropertyIA的body方法。根据前面的步骤3知道,NestedBasicPropertyIA的body方法作用主要是取出当前元素的值设置到父级元素的一个属性中,当前元素名称fileNamePattern,父级元素是rollingPolicy,利用反射调用父级元素的实例的setFileNamePattern,传入fileNamePattern的值。
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();
}
}