> 文章列表 > logback无法删除太久远的日志文件?logback删除日志文件源码分析

logback无法删除太久远的日志文件?logback删除日志文件源码分析

logback无法删除太久远的日志文件?logback删除日志文件源码分析

logback无法删除太久远的日志文件?logback删除日志文件源码分析

最近发现logback配置滚动日志,但是本地日志文件甚至还有2年前的日志文件,服务器是却是正常的!
网上搜索了一波没有发现,只找到说不能删除太久远的旧日志文件
logback无法删除太久远的日志文件?logback删除日志文件源码分析

我的配置

<!--    基于时间和空间的滚动日志,单个文件最大10MB,保留天数最大30天,所以日志保留最大空间是20GB    -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><fileNamePattern>/root/demo/logs/hornet_%d{yyyyMMdd}.%i.log</fileNamePattern><maxFileSize>10mb</maxFileSize><maxHistory>30</maxHistory><totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>

下面是源码分析

从配置可以看出,可以从 SizeAndTimeBasedRollingPolicy.class 着手

package ch.qos.logback.core.rolling;import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP.Usage;
import ch.qos.logback.core.util.FileSize;public class SizeAndTimeBasedRollingPolicy<E> extends TimeBasedRollingPolicy<E> {FileSize maxFileSize;@Overridepublic void start() {SizeAndTimeBasedFNATP<E> sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP<E>(Usage.EMBEDDED); if(maxFileSize == null) {addError("maxFileSize property is mandatory.");return;} else {addInfo("Archive files will be limited to ["+maxFileSize+"] each.");}sizeAndTimeBasedFNATP.setMaxFileSize(maxFileSize);timeBasedFileNamingAndTriggeringPolicy = sizeAndTimeBasedFNATP;if(!isUnboundedTotalSizeCap() && totalSizeCap.getSize() < maxFileSize.getSize()) {addError("totalSizeCap of ["+totalSizeCap+"] is smaller than maxFileSize ["+maxFileSize+"] which is non-sensical");return;}// most work is done by the parentsuper.start();}public void setMaxFileSize(FileSize aMaxFileSize) {this.maxFileSize = aMaxFileSize;}@Overridepublic String toString() {return "c.q.l.core.rolling.SizeAndTimeBasedRollingPolicy@"+this.hashCode();}
}

这个类什么也没写,那逻辑肯定在父类 TimeBasedRollingPolicy.class

public void start() {// set the LR for our utility objectrenameUtil.setContext(this.context);// find out period from the filename patternif (fileNamePatternStr != null) {fileNamePattern = new FileNamePattern(fileNamePatternStr, this.context);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 suffixfileNamePatternWithoutCompSuffix = new FileNamePattern(Compressor.computeFileNameStrWithoutCompSuffix(fileNamePatternStr, compressionMode), this.context);addInfo("Will use the pattern " + fileNamePatternWithoutCompSuffix + " for the active file");if (compressionMode == CompressionMode.ZIP) {String zipEntryFileNamePatternStr = transformFileNamePattern2ZipEntry(fileNamePatternStr);zipEntryFileNamePattern = new FileNamePattern(zipEntryFileNamePatternStr, context);}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;}//*********** 可以看到这里有 maxHistory,也就是最大保留天数//*********** 可以看到这里有 maxHistory,也就是最大保留天数//*********** 可以看到这里有 maxHistory,也就是最大保留天数//*********** 大概意思就是判断是否初始化,然后把参数设置到archiveRemover中,//*********** 后面调用archiveRemover.cleanAsynchronously方法进行清理!!!				// 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 (!isUnboundedTotalSizeCap()) {addWarn("'maxHistory' is not set, ignoring 'totalSizeCap' option with value ["+totalSizeCap+"]");}super.start();}

进入 TimeBasedArchiveRemover.class,可以看到这是一个异步任务,重点在 clean()

    public Future<?> cleanAsynchronously(Date now) {ArhiveRemoverRunnable runnable = new ArhiveRemoverRunnable(now);ExecutorService executorService = context.getScheduledExecutorService();Future<?> future = executorService.submit(runnable);return future;}public class ArhiveRemoverRunnable implements Runnable {Date now;ArhiveRemoverRunnable(Date now) {this.now = now;}@Overridepublic void run() {clean(now);if (totalSizeCap != UNBOUNDED_TOTAL_SIZE_CAP && totalSizeCap > 0) {capTotalSize(now);}}}

重点在 clean() ,清理,得到 periodsElapsed 天数,然后遍历清理 (-maxHistory+1)+i

    public void clean(Date now) {long nowInMillis = now.getTime();// for a live appender periodsElapsed is expected to be 1int periodsElapsed = computeElapsedPeriodsSinceLastClean(nowInMillis);lastHeartBeat = nowInMillis;if (periodsElapsed > 1) {addInfo("Multiple periods, i.e. " + periodsElapsed + " periods, seem to have elapsed. This is expected at application start.");}for (int i = 0; i < periodsElapsed; i++) {int offset = getPeriodOffsetForDeletionTarget() - i;Date dateOfPeriodToClean = rc.getEndOfNextNthPeriod(now, offset);cleanPeriod(dateOfPeriodToClean);}}

重要 computeElapsedPeriodsSinceLastClean() 计算 periodsElapsed 天数
UNINITIALIZED 先判断是否初始化
periodBarriersCrossed 计算过期出天数
INACTIVITY_TOLERANCE_IN_MILLIS 默认是32天(不知道为什么???

    int computeElapsedPeriodsSinceLastClean(long nowInMillis) {long periodsElapsed = 0;if (lastHeartBeat == UNINITIALIZED) {addInfo("first clean up after appender initialization");periodsElapsed = rc.periodBarriersCrossed(nowInMillis, nowInMillis + INACTIVITY_TOLERANCE_IN_MILLIS);periodsElapsed = Math.min(periodsElapsed, MAX_VALUE_FOR_INACTIVITY_PERIODS);} else {periodsElapsed = rc.periodBarriersCrossed(lastHeartBeat, nowInMillis);// periodsElapsed of zero is possible for size and time based policies}return (int) periodsElapsed;}

所以得出结论:logback只能删除最近-maxHistory天到-maxHistory-32天的日志文件

补充:

logback默认不会在项目启动时清理旧日志文件,如果需要启动执行则需配置

<cleanHistoryOnStart>true</cleanHistoryOnStart>