当前位置: 首页 > news >正文

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

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

    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;}// 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();}

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

// SizeAndTimeBasedFNATPpublic void start() {// we depend on certain fields having been initialized in super classsuper.start();archiveRemover = createArchiveRemover();archiveRemover.setContext(context);String regex = tbrp.fileNamePattern.toRegexForFixedDate(dateInCurrentPeriod);String stemRegex = FileFilterUtil.afterLastSlash(regex);computeCurrentPeriodsHighestCounterValue(stemRegex);}
// TimeBasedFileNamingAndTriggeringPolicyBasepublic 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();}

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

    protected void subAppend(E event) {synchronized (triggeringPolicy) {if (triggeringPolicy.isTriggeringEvent(currentlyActiveFile, event)) {rollover();}}super.subAppend(event);}

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

// TimeBasedRollingPolicypublic boolean isTriggeringEvent(File activeFile, final E event) {return timeBasedFileNamingAndTriggeringPolicy.isTriggeringEvent(activeFile, event);}
// SizeAndTimeBasedFNATPpublic boolean isTriggeringEvent(File activeFile, final E event) {long time = getCurrentTime();// first check for roll-over based on timeif (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;}

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

    public void rollover() {lock.lock();try {//关闭原始文件流this.closeOutputStream();//回滚attemptRollover();//新建日志文件attemptOpenFile();} finally {lock.unlock();}}

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

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

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

http://www.lryc.cn/news/305107.html

相关文章:

  • [C#]winform基于opencvsharp结合pairlie算法实现低光图像增强黑暗图片变亮变清晰
  • React18源码: reconcliler启动过程
  • 【RN】为项目使用React Navigation中的navigator
  • CS50x 2024 - Lecture 8 - HTML, CSS, JavaScript
  • C++:派生类的生成过程(构造、析构)
  • 金蝶字段添加过滤条件
  • SQLite 知识整理
  • 0基础JAVA期末复习最终版
  • 【办公类-16-07-04】合并版“2023下学期 中班户外游戏(有场地和无场地版,一周一次)”(python 排班表系列)
  • chat GPT第一讲
  • JAVA工程师面试专题-Mysql篇
  • vue中使用echarts绘制双Y轴图表时,刻度没有对齐的两种解决方法
  • 编程笔记 Golang基础 022 数组
  • 【kubernetes】二进制部署k8s集群之,多master节点负载均衡以及高可用(下)
  • 哈希表在Java中的使用和面试常见问题
  • LeetCode刷题小记 三、【哈希表】
  • Zookeeper选举Leader源码剖析
  • Redis(十六)缓存预热+缓存雪崩+缓存击穿+缓存穿透
  • [已解决]npm淘宝镜像最新官方指引(2023.08.31)
  • ffmpeg之avformat_alloc_output_context2
  • GitLab代码库提交量统计工具
  • Python爬虫技术详解:从基础到高级应用,实战与应对反爬虫策略【第93篇—Python爬虫】
  • 关于TypeReference的使用
  • 阿里大文娱前端一面
  • Clickhouse系列之连接工具连接、数据类型和数据库
  • 【深入理解设计模式】原型设计模式
  • Python算法题集_图论(课程表)
  • 视频评论挖掘软件|抖音视频下载工具
  • Linux学习方法-框架学习法——Linux驱动架构的演进
  • Spring Boot基础面试问题(一)