Logback 教程

Logback 笔记

logback maxHistory 属性设置不起作用的原因及解决方法

Logback 使用时,配置文件、日志级别、日志分割等问题的相关解答 Logback 使用时,配置文件、日志级别、日志分割等问题的相关解答


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");
        }
    }

    ......

}