logback 的日志,想要保留指定期间的内容,在 RollingPolicy 滚动策略中提供了 maxHistory 属性设置,但是发现它不能立即生效,貌似无效,不知为何?
解释
其实设置 maxHistory 不能起到立即生效的效果,看 logback 的源码 TimeBasedRollingPolicy 类的 rollover() 滚动方法可知,它会在进行滚动操作(如按天分割)的临界点时,才会触发异步的删除操作,源码如下:
public class TimeBasedRollingPolicy<E> extends RollingPolicyBase implements TriggeringPolicy<E> {
......
public void rollover() throws RolloverFailure {
String elapsedPeriodsFileName = this.timeBasedFileNamingAndTriggeringPolicy.getElapsedPeriodsFileName();
String elapsedPeriodStem = FileFilterUtil.afterLastSlash(elapsedPeriodsFileName);
if (this.compressionMode == CompressionMode.NONE) {
if (this.getParentsRawFileProperty() != null) {
this.renameUtil.rename(this.getParentsRawFileProperty(), elapsedPeriodsFileName);
}
} else if (this.getParentsRawFileProperty() == null) {
this.compressionFuture = this.compressor.asyncCompress(elapsedPeriodsFileName, elapsedPeriodsFileName, elapsedPeriodStem);
} else {
this.compressionFuture = this.renameRawAndAsyncCompress(elapsedPeriodsFileName, elapsedPeriodStem);
}
if (this.archiveRemover != null) {
Date now = new Date(this.timeBasedFileNamingAndTriggeringPolicy.getCurrentTime());
// 此处触发异步的删除操作
this.cleanUpFuture = this.archiveRemover.cleanAsynchronously(now);
}
}
......
}
方法真实调用 TimeBasedArchiveRemover 类的 cleanAsynchronously 方法,具体如下:
public class TimeBasedArchiveRemover extends ContextAwareBase implements ArchiveRemover {
......
private int maxHistory = 0;
......
void capTotalSize(Date now) {
long totalSize = 0L;
long totalRemoved = 0L;
for(int offset = 0; offset < this.maxHistory; ++offset) {
Date date = this.rc.getEndOfNextNthPeriod(now, -offset);
File[] matchingFileArray = this.getFilesInPeriod(date);
this.descendingSortByLastModified(matchingFileArray);
File[] arr$ = matchingFileArray;
int len$ = matchingFileArray.length;
for(int i$ = 0; i$ < len$; ++i$) {
File f = arr$[i$];
long size = f.length();
if (totalSize + size > this.totalSizeCap) {
this.addInfo("Deleting [" + f + "]" + " of size " + new FileSize(size));
totalRemoved += size;
// 真正删除操作
f.delete();
}
totalSize += size;
}
}
this.addInfo("Removed " + new FileSize(totalRemoved) + " of files");
}
......
public Future<?> cleanAsynchronously(Date now) {
TimeBasedArchiveRemover.ArhiveRemoverRunnable runnable = new TimeBasedArchiveRemover.ArhiveRemoverRunnable(now);
ExecutorService executorService = this.context.getScheduledExecutorService();
Future<?> future = executorService.submit(runnable);
return future;
}
public class ArhiveRemoverRunnable implements Runnable {
Date now;
ArhiveRemoverRunnable(Date now) {
this.now = now;
}
public void run() {
TimeBasedArchiveRemover.this.clean(this.now);
if (TimeBasedArchiveRemover.this.totalSizeCap != 0L && TimeBasedArchiveRemover.this.totalSizeCap > 0L) {
// 删除操作会进一步调用到这儿
TimeBasedArchiveRemover.this.capTotalSize(this.now);
}
}
}
}
立即生效
logback 针对 maxHistory 属性也提供了立即生效的配置 cleanHistoryOnStart
,其默认是 false,可以将其设置 true,这样程序启动时,立即会执行 maxHistory 的 check 工作及删除操作(如有必要),示例如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
......
<appender name="fileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M %L - %msg %n</Pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/file.%d{yyyyMMdd}.log</fileNamePattern>
<maxHistory>3</maxHistory>
<!-- 该处设置 -->
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</appender>
......
</configuration>
看源码也能看出来,还是 TimeBasedRollingPolicy 类,具体如下:
public class TimeBasedRollingPolicy<E> extends RollingPolicyBase implements TriggeringPolicy<E> {
......
boolean cleanHistoryOnStart = false;
public TimeBasedRollingPolicy() {
}
public void start() {
this.renameUtil.setContext(this.context);
if (this.fileNamePatternStr != null) {
this.fileNamePattern = new FileNamePattern(this.fileNamePatternStr, this.context);
this.determineCompressionMode();
this.compressor = new Compressor(this.compressionMode);
this.compressor.setContext(this.context);
this.fileNamePatternWithoutCompSuffix = new FileNamePattern(Compressor.computeFileNameStrWithoutCompSuffix(this.fileNamePatternStr, this.compressionMode), this.context);
this.addInfo("Will use the pattern " + this.fileNamePatternWithoutCompSuffix + " for the active file");
if (this.compressionMode == CompressionMode.ZIP) {
String zipEntryFileNamePatternStr = this.transformFileNamePattern2ZipEntry(this.fileNamePatternStr);
this.zipEntryFileNamePattern = new FileNamePattern(zipEntryFileNamePatternStr, this.context);
}
if (this.timeBasedFileNamingAndTriggeringPolicy == null) {
this.timeBasedFileNamingAndTriggeringPolicy = new DefaultTimeBasedFileNamingAndTriggeringPolicy();
}
this.timeBasedFileNamingAndTriggeringPolicy.setContext(this.context);
this.timeBasedFileNamingAndTriggeringPolicy.setTimeBasedRollingPolicy(this);
this.timeBasedFileNamingAndTriggeringPolicy.start();
if (!this.timeBasedFileNamingAndTriggeringPolicy.isStarted()) {
this.addWarn("Subcomponent did not start. TimeBasedRollingPolicy will not start.");
} else {
if (this.maxHistory != 0) {
this.archiveRemover = this.timeBasedFileNamingAndTriggeringPolicy.getArchiveRemover();
this.archiveRemover.setMaxHistory(this.maxHistory);
this.archiveRemover.setTotalSizeCap(this.totalSizeCap.getSize());
if (this.cleanHistoryOnStart) {
// 若设置 true,会执行该段代码
this.addInfo("Cleaning on start up");
Date now = new Date(this.timeBasedFileNamingAndTriggeringPolicy.getCurrentTime());
this.cleanUpFuture = this.archiveRemover.cleanAsynchronously(now);
}
} else if (!this.isUnboundedTotalSizeCap()) {
this.addWarn("'maxHistory' is not set, ignoring 'totalSizeCap' option with value [" + this.totalSizeCap + "]");
}
super.start();
}
} else {
this.addWarn("The FileNamePattern option must be set before using TimeBasedRollingPolicy. ");
this.addWarn("See also http://logback.qos.ch/codes.html#tbr_fnp_not_set");
throw new IllegalStateException("The FileNamePattern option must be set before using TimeBasedRollingPolicy. See also http://logback.qos.ch/codes.html#tbr_fnp_not_set");
}
}
......
}