• 聊聊logback的TimeBasedRollingPolicy


    本文主要研究一下logback的TimeBasedRollingPolicy

    TimeBasedRollingPolicy

    public class TimeBasedRollingPolicy extends RollingPolicyBase implements TriggeringPolicy {
        static final String FNP_NOT_SET = "The FileNamePattern option must be set before using TimeBasedRollingPolicy. ";
        // WCS: without compression suffix
        FileNamePattern fileNamePatternWithoutCompSuffix;
    
        private Compressor compressor;
        private RenameUtil renameUtil = new RenameUtil();
        Future compressionFuture;
        Future cleanUpFuture;
    
        private int maxHistory = UNBOUNDED_HISTORY;
        protected FileSize totalSizeCap = new FileSize(UNBOUNDED_TOTAL_SIZE_CAP);
    
        private ArchiveRemover archiveRemover;
    
        TimeBasedFileNamingAndTriggeringPolicy timeBasedFileNamingAndTriggeringPolicy;
    
        boolean cleanHistoryOnStart = false;
    
        //......
    }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    TimeBasedRollingPolicy继承了RollingPolicyBase,它定义了maxHistory、cleanHistoryOnStart、timeBasedFileNamingAndTriggeringPolicy等属性

    start

        public void start() {
            // set the LR for our utility object
            renameUtil.setContext(this.context);
    
            // find out period from the filename pattern
            if (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 suffix
            fileNamePatternWithoutCompSuffix = 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<>();
            }
            timeBasedFileNamingAndTriggeringPolicy.setContext(context);
            timeBasedFileNamingAndTriggeringPolicy.setTimeBasedRollingPolicy(this);
            timeBasedFileNamingAndTriggeringPolicy.start();
    
            if (!timeBasedFileNamingAndTriggeringPolicy.isStarted()) {
                addWarn("Subcomponent did not start. TimeBasedRollingPolicy will not start.");
                return;
            }
    
            // 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 != UNBOUNDED_HISTORY) {
                archiveRemover = timeBasedFileNamingAndTriggeringPolicy.getArchiveRemover();
                archiveRemover.setMaxHistory(maxHistory);
                archiveRemover.setTotalSizeCap(totalSizeCap.getSize());
                if (cleanHistoryOnStart) {
                    addInfo("Cleaning on start up");
                    Instant now = Instant.ofEpochMilli(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime());
                    cleanUpFuture = archiveRemover.cleanAsynchronously(now);
                }
            } else if (!isUnboundedTotalSizeCap()) {
                addWarn("'maxHistory' is not set, ignoring 'totalSizeCap' option with value [" + totalSizeCap + "]");
            }
    
            super.start();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    start方法根据fileNamePatternStr创建FileNamePattern,根据compressionMode创建Compressor,对于zip压缩的创建zipEntryFileNamePattern,另外默认设置了DefaultTimeBasedFileNamingAndTriggeringPolicy,然后执行其start,对于maxHistory不为0的,则设置archiveRemover

    stop

       public void stop() {
            if (!isStarted())
                return;
            waitForAsynchronousJobToStop(compressionFuture, "compression");
            waitForAsynchronousJobToStop(cleanUpFuture, "clean-up");
            super.stop();
        }
    
        private void waitForAsynchronousJobToStop(Future aFuture, String jobDescription) {
            if (aFuture != null) {
                try {
                    aFuture.get(CoreConstants.SECONDS_TO_WAIT_FOR_COMPRESSION_JOBS, TimeUnit.SECONDS);
                } catch (TimeoutException e) {
                    addError("Timeout while waiting for " + jobDescription + " job to finish", e);
                } catch (Exception e) {
                    addError("Unexpected exception while waiting for " + jobDescription + " job to finish", e);
                }
            }
        }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    stop方法执行waitForAsynchronousJobToStop,主要是等待compressionFuture及cleanUpFuture

    rollover

        public void rollover() throws RolloverFailure {
    
            // when rollover is called the elapsed period's file has
            // been already closed. This is a working assumption of this method.
    
            String elapsedPeriodsFileName = timeBasedFileNamingAndTriggeringPolicy.getElapsedPeriodsFileName();
    
            String elapsedPeriodStem = FileFilterUtil.afterLastSlash(elapsedPeriodsFileName);
    
            if (compressionMode == CompressionMode.NONE) {
                if (getParentsRawFileProperty() != null) {
                    renameUtil.rename(getParentsRawFileProperty(), elapsedPeriodsFileName);
                } // else { nothing to do if CompressionMode == NONE and parentsRawFileProperty ==
                  // null }
            } else {
                if (getParentsRawFileProperty() == null) {
                    compressionFuture = compressor.asyncCompress(elapsedPeriodsFileName, elapsedPeriodsFileName,
                            elapsedPeriodStem);
                } else {
                    compressionFuture = renameRawAndAsyncCompress(elapsedPeriodsFileName, elapsedPeriodStem);
                }
            }
    
            if (archiveRemover != null) {
                Instant now = Instant.ofEpochMilli(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime());
                this.cleanUpFuture = archiveRemover.cleanAsynchronously(now);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    rollover方法通过timeBasedFileNamingAndTriggeringPolicy获取elapsedPeriodsFileName,然后将当前文件重命名为elapsedPeriodsFileName,对于archiveRemover不为null的则执行cleanAsynchronously

    ArchiveRemover

    ch/qos/logback/core/rolling/helper/ArchiveRemover.java

    public interface ArchiveRemover extends ContextAware {
        void clean(Instant instant);
    
        void setMaxHistory(int maxHistory);
    
        void setTotalSizeCap(long totalSizeCap);
    
        Future cleanAsynchronously(Instant now);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    ArchiveRemover定义了clean、setMaxHistory、setTotalSizeCap、cleanAsynchronously方法

    TimeBasedArchiveRemover

    ch/qos/logback/core/rolling/helper/TimeBasedArchiveRemover.java

    public class TimeBasedArchiveRemover extends ContextAwareBase implements ArchiveRemover {
    
        static protected final long UNINITIALIZED = -1;
        // aim for 32 days, except in case of hourly rollover, see
        // MAX_VALUE_FOR_INACTIVITY_PERIODS
        static protected final long INACTIVITY_TOLERANCE_IN_MILLIS = 32L * (long) CoreConstants.MILLIS_IN_ONE_DAY;
        static final int MAX_VALUE_FOR_INACTIVITY_PERIODS = 14 * 24; // 14 days in case of hourly rollover
    
        final FileNamePattern fileNamePattern;
        final RollingCalendar rc;
        private int maxHistory = CoreConstants.UNBOUNDED_HISTORY;
        private long totalSizeCap = CoreConstants.UNBOUNDED_TOTAL_SIZE_CAP;
        final boolean parentClean;
        long lastHeartBeat = UNINITIALIZED;
    
        public TimeBasedArchiveRemover(FileNamePattern fileNamePattern, RollingCalendar rc) {
            this.fileNamePattern = fileNamePattern;
            this.rc = rc;
            this.parentClean = computeParentCleaningFlag(fileNamePattern);
        }
    
        //......
    }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    TimeBasedArchiveRemover定义了fileNamePattern、rollingCalendar、maxHistory、totalSizeCap属性

    clean

        public void clean(Instant now) {
    
            long nowInMillis = now.toEpochMilli();
            // 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;
                Instant instantOfPeriodToClean = rc.getEndOfNextNthPeriod(now, offset);
                cleanPeriod(instantOfPeriodToClean);
            }
        }
    
        public void cleanPeriod(Instant instantOfPeriodToClean) {
            File[] matchingFileArray = getFilesInPeriod(instantOfPeriodToClean);
    
            for (File f : matchingFileArray) {
                addInfo("deleting " + f);
                f.delete();
            }
    
            if (parentClean && matchingFileArray.length > 0) {
                File parentDir = getParentDir(matchingFileArray[0]);
                removeFolderIfEmpty(parentDir);
            }
        }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    clean方法主要是计算periodsElapsed,然后通过rollingCalendar获取instantOfPeriodToClean,再执行cleanPeriod方法;cleanPeriod方法则通过getFilesInPeriod获取对应的文件,然后挨个执行delete,最后再判断下parentDir是否为空,为空则删除

    cleanAsynchronously

        public Future cleanAsynchronously(Instant now) {
            ArhiveRemoverRunnable runnable = new ArhiveRemoverRunnable(now);
            ExecutorService executorService = context.getExecutorService();
            Future future = executorService.submit(runnable);
            return future;
        }
    
        public class ArhiveRemoverRunnable implements Runnable {
            Instant now;
    
            ArhiveRemoverRunnable(Instant now) {
                this.now = now;
            }
    
            @Override
            public void run() {
                clean(now);
                if (totalSizeCap != UNBOUNDED_TOTAL_SIZE_CAP && totalSizeCap > 0) {
                    capTotalSize(now);
                }
            }
        }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    cleanAsynchronously主要是创建ArhiveRemoverRunnable,然后提交到context的executorService;ArhiveRemoverRunnable实现了Runnable接口,其run方法执行clean,对于totalSizeCap大于0的执行capTotalSize

    小结

    TimeBasedRollingPolicy包含了RollingPolicy及TriggeringPolicy,其rollover方法主要是委托给timeBasedFileNamingAndTriggeringPolicy获取elapsedPeriodsFileName然后去rename,对于maxHistory不是无限制的设置timeBasedFileNamingAndTriggeringPolicy的archiveRemover的maxHistory及totalSizeCap,执行其cleanAsynchronously方法;其isTriggeringEvent方法也是委托给了timeBasedFileNamingAndTriggeringPolicy。

  • 相关阅读:
    【云原生 | 从零开始学Kubernetes】二、使用kubeadm搭建K8S集群
    freeRTOS学习(一)
    Django DRF版本号的处理
    如何掌握HEC-RAS建模方法与涉河建设项目防洪评价报告编制
    alpha融合详解(alpha compositing)
    Macbook Pro 删除启动台无效图标
    网络编程之NIO 基础
    基于Python实现的KNN分类实验
    面试题:Promise用法及理解
    不需要服务器,教你仅用30行代码搞定实时健康码识别
  • 原文地址:https://blog.csdn.net/hello_ejb3/article/details/134324894