当前位置:   article > 正文

logback日志回滚原理

logback日志回滚原理

日志输出主要依赖RollingFileAppender、TimeBasedRollingPolicy、SizeAndTimeBasedFNATP。

  • RollingFileAppender
    主要用于生成日志文件,格式化内容再输出到日志文件
  • TimeBasedRollingPolicy
    设置回滚策略,如果发现日志输出的时间超过单位时间,则进行回滚,在RollingFileAppender的日志文件添加FileNamePattern后缀,同时清理掉MaxHistory时间之前的日志。例如如果fileNamePattern是%d{yyyy-MM-dd_HH-mm}.%i.gz,最后的时间单位是分钟,则在每一分钟之后进行回滚,将原始日志文件后打成gz压缩包,同时添加yyyy-MM-dd_HH-mm.i.gz作为后缀。如果maxHistory的值是30,则会在回滚时删除30分钟之前的日志。
  • SizeAndTimeBasedFNATP
    基于文件大小进行回滚,例如maxFileSize的值为1MB,则当文件大小超过1MB时进行回滚。
        // RollingFileAppender用于定义日志输出的格式和路径
        RollingFileAppender<Object> rollingFileAppender = (RollingFileAppender<Object>) appender;
        rollingFileAppender.setContext(getContext());
        rollingFileAppender.setLayout(layout);
        if (getFileNamePattern() != null) {
            // 基于时间的回滚策略
            TimeBasedRollingPolicy<Object> policy = new TimeBasedRollingPolicy<Object>();
            policy.setFileNamePattern(rollingFileAppender.rawFileProperty()+"."+getFileNamePattern());
            policy.setContext(getContext());
            policy.setMaxHistory(getMaxHistory());
            policy.setParent(rollingFileAppender);
            if (getMaxFileSize() != null) {
                //基于文件大小的回滚策略
                SizeAndTimeBasedFNATP<Object> triggeringPolicy = new SizeAndTimeBasedFNATP<Object>();
                triggeringPolicy.setMaxFileSize(FileSize.valueOf(getMaxFileSize()));
                triggeringPolicy.setTimeBasedRollingPolicy(policy);
                triggeringPolicy.setContext(getContext());
                policy.setTimeBasedFileNamingAndTriggeringPolicy(triggeringPolicy);
            }
            policy.start();
            rollingFileAppender.setRollingPolicy(policy);
            rollingFileAppender.start();
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

在TimeBasedRollingPolicy#start()中,主要是根据FileNamePattern的后缀名生成Compressor压缩对象,用来压缩日志文件,同时启动TimeBasedFileNamingAndTriggeringPolicy,TimeBasedFileNamingAndTriggeringPolicy是用来根据时间找到日志并清理的对象。

    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<E>();
        }
        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 != 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();
    }
  • 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

TimeBasedFileNamingAndTriggeringPolicy对应的实现是SizeAndTimeBasedFNATP,因此start()方法会被调用,主要就是设置日志文件的起始时间,计算下次回滚的时间computeNextCheck(),当时间超过时就会进行回滚,同时创建ArchiveRemover用于删除日志文件。

// SizeAndTimeBasedFNATP
    public void start() {
        // we depend on certain fields having been initialized in super class
        super.start();

        archiveRemover = createArchiveRemover();
        archiveRemover.setContext(context);

        String regex = tbrp.fileNamePattern.toRegexForFixedDate(dateInCurrentPeriod);
        String stemRegex = FileFilterUtil.afterLastSlash(regex);

        computeCurrentPeriodsHighestCounterValue(stemRegex);

    }
// TimeBasedFileNamingAndTriggeringPolicyBase
    public void start() {
        DateTokenConverter<Object> dtc = tbrp.fileNamePattern.getPrimaryDateTokenConverter();
        if (dtc == null) {
            throw new IllegalStateException("FileNamePattern [" + tbrp.fileNamePattern.getPattern() + "] does not contain a valid DateToken");
        }

        if (dtc.getTimeZone() != null) {
            rc = new RollingCalendar(dtc.getDatePattern(), dtc.getTimeZone(), Locale.getDefault());
        } else {
            rc = new RollingCalendar(dtc.getDatePattern());
        }
        addInfo("The date pattern is '" + dtc.getDatePattern() + "' from file name pattern '" + tbrp.fileNamePattern.getPattern() + "'.");
        rc.printPeriodicity(this);

        if (!rc.isCollisionFree()) {
            addError("The date format in FileNamePattern will result in collisions in the names of archived log files.");
            addError(CoreConstants.MORE_INFO_PREFIX + COLLIDING_DATE_FORMAT_URL);
            withErrors();
            return;
        }

        setDateInCurrentPeriod(new Date(getCurrentTime()));
        if (tbrp.getParentsRawFileProperty() != null) {
            File currentFile = new File(tbrp.getParentsRawFileProperty());
            if (currentFile.exists() && currentFile.canRead()) {
                setDateInCurrentPeriod(new Date(currentFile.lastModified()));
            }
        }
        addInfo("Setting initial period to " + dateInCurrentPeriod);
        computeNextCheck();
    }

  • 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

到这里,模块启动完毕,开始正式的处理日志,处理日志是从RollingFileAppender#doAppend()方法开始,最终是到RollingFileAppender#subAppend()方法开始进行正式的处理,着重分析这个方法即可。这个方法逻辑很简单,先判断是否能够进行回滚,然后rollover()回滚处理,再调用父类OutputStreamAppender#subAppend()输出内容到日志文件,我们主要关系回滚过程。

    protected void subAppend(E event) {
        synchronized (triggeringPolicy) {
            if (triggeringPolicy.isTriggeringEvent(currentlyActiveFile, event)) {
                rollover();
            }
        }
        super.subAppend(event);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

首先根据TimeBasedRollingPolicy#isTriggeringEvent()判断是否能够进行回滚,会调用内置的TimeBasedFileNamingAndTriggeringPolicy对象进行判断,在两种情况下会进行回滚,一是如果时间到了,二是文件超过我们设置的maxFileSize。

// TimeBasedRollingPolicy
    public boolean isTriggeringEvent(File activeFile, final E event) {
        return timeBasedFileNamingAndTriggeringPolicy.isTriggeringEvent(activeFile, event);
    }
// SizeAndTimeBasedFNATP
    public boolean isTriggeringEvent(File activeFile, final E event) {

        long time = getCurrentTime();

        // first check for roll-over based on time
        if (time >= nextCheck) {
            Date dateInElapsedPeriod = dateInCurrentPeriod;
            elapsedPeriodsFileName = tbrp.fileNamePatternWithoutCompSuffix.convertMultipleArguments(dateInElapsedPeriod, currentPeriodsCounter);
            currentPeriodsCounter = 0;
            setDateInCurrentPeriod(time);
            computeNextCheck();
            return true;
        }

......
        if (activeFile.length() >= maxFileSize.getSize()) {

            elapsedPeriodsFileName = tbrp.fileNamePatternWithoutCompSuffix.convertMultipleArguments(dateInCurrentPeriod, currentPeriodsCounter);
            currentPeriodsCounter++;
            return true;
        }

        return false;
    }
  • 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

回滚的处理是在RollingFileAppender#rollover()中。

    public void rollover() {
        lock.lock();
        try {
            //关闭原始文件流
            this.closeOutputStream();
            //回滚
            attemptRollover();
            //新建日志文件
            attemptOpenFile();
        } finally {
            lock.unlock();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

首先获取回滚时生成的文件名,然后进行回滚,如果没有指定压缩策略的就重命名,指定了就进行压缩。

    public void rollover() throws RolloverFailure {

        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) {
            Date now = new Date(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

如果需要进行定制的话,可以考虑重写RollingFileAppender、TimeBasedRollingPolicy,让日志按照我们的期望输出。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/138962
推荐阅读
相关标签
  

闽ICP备14008679号