logback中 maxHistory 属性无效

1. 描述

logback的滚动策略中配置了 maxHistory 之后,日志目录下依旧存在很多(超出策略时间之外)日志。

  • 环境
IDE: 2021.3
spring boot: 2.5.6 (spring-boot-starter-logging: 2.5.6 --- logback-classic: 1.2.6)
  • logback配置:
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
	<FileNamePattern>${LOG_PATH}/error/${LOG_NAME}_error.%d{yyyy-MM-dd}.part_%i.log.zip</FileNamePattern>
	<!--日志文件保留天数(maxHistory属性优先级高于totalSizeCap)-->
	<maxHistory>3</maxHistory>
	<!-- 总文件超出大小删除老文件(需要设置maxHistory后生效) -->
	<totalSizeCap>20KB</totalSizeCap>
	<maxFileSize>10KB</maxFileSize>
	<!-- appender启动时,进行一次日志文件清理(作用:有些存活很短时间的应用,没机会进行文件清理) -->
	<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>

2. 问题

  • 配置策略,根据时间滚动,保留3天内的日志,如图,依然存在很多以前的日志文件。

  • 原因: 默认第一次只清理32天内的日志内容,之后清理上一次处理之后的。超过时间之前的日志文件,不在清理范围内,所以没被清理。

  • ```

3. 日志清理流程

1. 获取时间段(当前时间到上一次清理时间):日志文件清理仅限于此时间范围。
2. 计算周期(时间段内有多少个周期):如:第一次,按天就是32,按小时就是 32 * 243. 找出每个周期内的日志文件。
4. 删除日志文件。
  • eg:SizeAndTimeBasedRollingPolicy策略,代码如下
   // 文件删除策略:TimeBasedArchiveRemover
   public void clean(Date now) {
       long nowInMillis = now.getTime();
       // for a live appender periodsElapsed is expected to be 1
       // 计算周期
       int 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);
       }
   }

   // 计算周期
   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;
   }