[url=http://czj4451.iteye.com/blog/1974831]logback系列之一:输出日志到控制台[/url]
[url=http://czj4451.iteye.com/blog/1975220]logback系列之二:输出日志到文件[/url]
logback系列之三:输出日志到文件(滚动)
[url=http://czj4451.iteye.com/blog/1984259]logback系列之四:输出日志到不同文件[/url]
实际生产中,每天都有大量的日志生成,单个文件(FileAppender)已经不能满足要求,RollingFileAppender继承了FileAppender,并提供了更多的功能:
[list]
[*] 每天生成一个日志文件
[*] 将前一天的日志重命名为包含日期的格式
[*] 根据需要,删除过期历史日志
[/list]
[color=darkred][size=medium]配置[/size][/color]
和[url=http://czj4451.iteye.com/blog/1975220]logback系列之二:输出日志到文件[/url]类似,改动的地方:
[color=orange]1. logback[-test].xml文件:[/color]
调用测试类的方法,生成heuristic-2013-11-16.log文件。
[color=darkred][size=medium]源码分析[/size][/color]
[size=medium][color=blue]a[/color]. 如果指定了file属性,当天的文件名为file属性值(/logs/heuristic.log):[/size]
[b]ch.qos.logback.core.rolling.RollingFileAppender类[/b]
上面logback[-test].xml的rollingPolicy配置为
[b]ch.qos.logback.core.rolling.TimeBasedRollingPolicy类[/b]
看下实现:
[b]ch.qos.logback.core.FileAppender类[/b]
[size=medium][color=blue]b[/color]. [color=red]必须指定TriggeringPolicy<E>和RollingPolicy[/color],否则不会打印日志:[/size]
看下
[b]ch.qos.logback.core.rolling.RollingFileAppender类[/b]:
TimeBasedRollingPolicy类实现了上面的两个接口,triggeringPolicy和rollingPolicy都指向TimeBasedRollingPolicy的实例对象。
[size=medium][color=blue]c[/color]. 如果file指定,前一天的文件名改为[color=red]fileNamePattern[/color]的值(/logs/heuristic-2013-11-15.log)。[/size]
通过设置TimeBasedRollingPolicy的[color=red]maxHistory属性[/color],可以控制已产生日志的数量。如果maxHistory设置为30,那么超过30天的log文件会被自动删除,这是通过RollingFileAppender的[color=red]rollover()[/color]方法来实现的。
该方法会调用
[b]ch.qos.logback.core.rolling.TimeBasedRollingPolicy类[/b]
的rollover方法:
默认的archiveRemover为[b]ch.qos.logback.core.rolling.helper.DefaultArchiveRemover抽象类[/b]:
[code]
public void clean(Date now) {
long nowInMillis = now.getTime();
int periodsElapsed = computeElapsedPeriodsSinceLastClean(nowInMillis);
lastHeartBeat = nowInMillis;
if (periodsElapsed > 1) {
addInfo("periodsElapsed = " + periodsElapsed);
}
// 从待删除日志的终止日期开始,每次回退一天,删除过期日志
for (int i = 0; i < periodsElapsed; i++) {
cleanByPeriodOffset(now, periodOffsetForDeletionTarget - i);
}
}
public void setMaxHistory(int maxHistory) {
// 待删除日志的终止日期:回退(maxHistory+1)天
this.periodOffsetForDeletionTarget = -maxHistory - 1;
}
[/code]
cleanByPeriodOffset为抽象方法,这里交由
[b]ch.qos.logback.core.rolling.helper.TimeBasedArchiveRemover子类[/b]实现:
[size=medium][color=blue]d[/color]. 如果要启用压缩,需要将[color=red]fileNamePattern的后缀名[/color]设置为压缩格式,如:/logs/heuristic-%d{yyyy-MM-dd}.zip。[/size]
看下[b]ch.qos.logback.core.rolling.RollingPolicyBase类[/b]相关的代码:
[size=medium][color=blue]e[/color]. RollingFileAppender保证append的值为true,这样[b]当天先写入的日志内容就不会丢失[/b]:[/size]
[b]ch.qos.logback.core.rolling.RollingFileAppender类[/b]的start()方法:
[size=medium][color=blue]f[/color]. [color=red]prudent[/color]模式不支持file设定;[b]如果启用了压缩,logback将不会记录日志[/b]:[/size]
[b]ch.qos.logback.core.rolling.RollingFileAppender类[/b]
的start()方法:
[url=http://czj4451.iteye.com/blog/1975220]logback系列之二:输出日志到文件[/url]
logback系列之三:输出日志到文件(滚动)
[url=http://czj4451.iteye.com/blog/1984259]logback系列之四:输出日志到不同文件[/url]
实际生产中,每天都有大量的日志生成,单个文件(FileAppender)已经不能满足要求,RollingFileAppender继承了FileAppender,并提供了更多的功能:
[list]
[*] 每天生成一个日志文件
[*] 将前一天的日志重命名为包含日期的格式
[*] 根据需要,删除过期历史日志
[/list]
[color=darkred][size=medium]配置[/size][/color]
和[url=http://czj4451.iteye.com/blog/1975220]logback系列之二:输出日志到文件[/url]类似,改动的地方:
[color=orange]1. logback[-test].xml文件:[/color]
<appender name="rollingAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/logs/heuristic.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/logs/heuristic-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder><!-- 必须指定,否则不会往文件输出内容 -->
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n</pattern>
</encoder>
<append>false</append>
<prudent>false</prudent>
</appender>
<root level="DEBUG">
<appender-ref ref="rollingAppender" />
</root>
调用测试类的方法,生成heuristic-2013-11-16.log文件。
[color=darkred][size=medium]源码分析[/size][/color]
[size=medium][color=blue]a[/color]. 如果指定了file属性,当天的文件名为file属性值(/logs/heuristic.log):[/size]
[b]ch.qos.logback.core.rolling.RollingFileAppender类[/b]
public void start() {
...
currentlyActiveFile = new File(getFile()); // 获取日志文件名
}
public String getFile() {
return rollingPolicy.getActiveFileName(); // 从滚动策略获取
}
上面logback[-test].xml的rollingPolicy配置为
[b]ch.qos.logback.core.rolling.TimeBasedRollingPolicy类[/b]
看下实现:
public String getActiveFileName() {
String parentsRawFileProperty = getParentsRawFileProperty();
if (parentsRawFileProperty != null) { // logback.xml指定了file属性
return parentsRawFileProperty; // 使用file值
} else {
return timeBasedFileNamingAndTriggeringPolicy
.getCurrentPeriodsFileNameWithoutCompressionSuffix();
}
}
public String getParentsRawFileProperty() {
return parent.rawFileProperty();
}
[b]ch.qos.logback.core.FileAppender类[/b]
final public String rawFileProperty() {
return fileName;
}
[size=medium][color=blue]b[/color]. [color=red]必须指定TriggeringPolicy<E>和RollingPolicy[/color],否则不会打印日志:[/size]
看下
[b]ch.qos.logback.core.rolling.RollingFileAppender类[/b]:
public void start() {
if (triggeringPolicy == null) {
addWarn("No TriggeringPolicy was set for the RollingFileAppender named "
+ getName());
addWarn("For more information, please visit "+CODES_URL+"#rfa_no_tp");
return;
}
if (rollingPolicy == null) {
addError("No RollingPolicy was set for the RollingFileAppender named "
+ getName());
addError("For more information, please visit "+CODES_URL+"rfa_no_rp");
return;
}
TimeBasedRollingPolicy类实现了上面的两个接口,triggeringPolicy和rollingPolicy都指向TimeBasedRollingPolicy的实例对象。
[size=medium][color=blue]c[/color]. 如果file指定,前一天的文件名改为[color=red]fileNamePattern[/color]的值(/logs/heuristic-2013-11-15.log)。[/size]
通过设置TimeBasedRollingPolicy的[color=red]maxHistory属性[/color],可以控制已产生日志的数量。如果maxHistory设置为30,那么超过30天的log文件会被自动删除,这是通过RollingFileAppender的[color=red]rollover()[/color]方法来实现的。
该方法会调用
[b]ch.qos.logback.core.rolling.TimeBasedRollingPolicy类[/b]
的rollover方法:
public void rollover() throws RolloverFailure {
// 当此方法调用时,假定前一天日志文件已经关闭
String elapsedPeriodsFileName = timeBasedFileNamingAndTriggeringPolicy
.getElapsedPeriodsFileName();
String elpasedPeriodStem = FileFilterUtil.afterLastSlash(elapsedPeriodsFileName);
if (compressionMode == CompressionMode.NONE) {
if (getParentsRawFileProperty() != null) {
renameUtil.rename(getParentsRawFileProperty(), elapsedPeriodsFileName); // file指定,重命名为fileNamePattern格式。如果目标文件存在,则重命名失败
} // else { nothing to do if CompressionMode == NONE and parentsRawFileProperty == null }
} else {
if (getParentsRawFileProperty() == null) {
future = asyncCompress(elapsedPeriodsFileName, elapsedPeriodsFileName, elpasedPeriodStem); // file未指定,异步压缩
} else {
future = renamedRawAndAsyncCompress(elapsedPeriodsFileName, elpasedPeriodStem); // file指定,重命名并异步压缩
}
}
if (archiveRemover != null) {
archiveRemover.clean(new Date(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime())); // 删除历史日志
}
}
默认的archiveRemover为[b]ch.qos.logback.core.rolling.helper.DefaultArchiveRemover抽象类[/b]:
[code]
public void clean(Date now) {
long nowInMillis = now.getTime();
int periodsElapsed = computeElapsedPeriodsSinceLastClean(nowInMillis);
lastHeartBeat = nowInMillis;
if (periodsElapsed > 1) {
addInfo("periodsElapsed = " + periodsElapsed);
}
// 从待删除日志的终止日期开始,每次回退一天,删除过期日志
for (int i = 0; i < periodsElapsed; i++) {
cleanByPeriodOffset(now, periodOffsetForDeletionTarget - i);
}
}
public void setMaxHistory(int maxHistory) {
// 待删除日志的终止日期:回退(maxHistory+1)天
this.periodOffsetForDeletionTarget = -maxHistory - 1;
}
[/code]
cleanByPeriodOffset为抽象方法,这里交由
[b]ch.qos.logback.core.rolling.helper.TimeBasedArchiveRemover子类[/b]实现:
protected void cleanByPeriodOffset(Date now, int periodOffset) {
Date date2delete = rc.getRelativeDate(now, periodOffset); // 计算需要删除日志的日期
String filename = fileNamePattern.convert(date2delete);
File file2Delete = new File(filename);
if (file2Delete.exists() && file2Delete.isFile()) {
file2Delete.delete(); // 删除文件
addInfo("deleting " + file2Delete);
if (parentClean) {
removeFolderIfEmpty(file2Delete.getParentFile());
}
}
}
[size=medium][color=blue]d[/color]. 如果要启用压缩,需要将[color=red]fileNamePattern的后缀名[/color]设置为压缩格式,如:/logs/heuristic-%d{yyyy-MM-dd}.zip。[/size]
看下[b]ch.qos.logback.core.rolling.RollingPolicyBase类[/b]相关的代码:
protected void determineCompressionMode() {
if (fileNamePatternStr.endsWith(".gz")) {
addInfo("Will use gz compression");
compressionMode = CompressionMode.GZ;
} else if (fileNamePatternStr.endsWith(".zip")) {
addInfo("Will use zip compression");
compressionMode = CompressionMode.ZIP;
} else { // 如果后缀名不是.gz或.zip,不会启用压缩
addInfo("No compression will be used");
compressionMode = CompressionMode.NONE;
}
}
[size=medium][color=blue]e[/color]. RollingFileAppender保证append的值为true,这样[b]当天先写入的日志内容就不会丢失[/b]:[/size]
[b]ch.qos.logback.core.rolling.RollingFileAppender类[/b]的start()方法:
if (!append) { // append为false时
addWarn("Append mode is mandatory for RollingFileAppender");
append = true; // 改成true
}
[size=medium][color=blue]f[/color]. [color=red]prudent[/color]模式不支持file设定;[b]如果启用了压缩,logback将不会记录日志[/b]:[/size]
[b]ch.qos.logback.core.rolling.RollingFileAppender类[/b]
的start()方法:
if (isPrudent()) { // 安全模式,但效率低
if (rawFileProperty() != null) {
addWarn("Setting \"File\" property to null on account of prudent mode");
setFile(null); // 取消file属性设置
}
if (rollingPolicy.getCompressionMode() != CompressionMode.NONE) { // 启用了压缩
addError("Compression is not supported in prudent mode. Aborting");
return; // 返回,不会进行后面的记录日志操作
}
}