logback服务器日志删除原理分析
创始人
2025-01-20 07:03:29
0

    查看以下的logback官方文档

Chapter 4: Appendersicon-default.png?t=N7T8https://logback.qos.ch/manual/appenders.html

    按文档说明,maxHistory是设置保存归档日志的最大数量,该数量的单位受到fileNamePattern里的值%d控制,如果有多个%d,只能有一个主%d,其他的要用aux参数标记为辅助令牌。

/var/log/%d{yyyy/MM, aux}/myapplication.%d{yyyy-MM-dd}.log

比如上面的fileNamePattern主%d是%d{yyyy-MM-dd},意味着显示的文件名模式按年份和月份组织日志文件夹,但每天午夜滚动日志文件。

也就是说maxHistory是归档日志的最大数量,该数量的单位可以是多种,类型如下

        小时、天、周、毫秒、秒、分钟、半天、月。

单位受%d控制。

       cleanHistoryOnStart参数用于启动时删除需要删除日志文件,如果不配置默认是false,意味着启动时不删除日志。

在项目中我们发现当触发日志删除条件时,一些历史久远的日志无法删除。那日志的删除逻辑时怎么样的?下面我们先做了逻辑总结,各位有兴趣可以查看下面的源码分析。

       logback是无法删除历史很久远的日志的。比如maxHistory设置为30,单位设置为日。那么执行时,根据单位删除的是距离今天31天前的到距离今天62(31+32-1)天前(的日志。

例如:今天是2023年10月16日删除的是31天前(2023年9月15日)到62天前(2023年8月15日)的日志。

这部分逻辑见以下源码分析:

以下是日志配置文件:

           MyContextName                                                                                                                                                               debug                                            ${CONSOLE_LOG_PATTERN}                          UTF-8                                                    ${logging.path}/web_debug.log                               %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n             UTF-8                                                                            ${logging.path}/web-debug-%d{yyyy-MM-dd}.%i.log                                           100MB                                       30                                                 debug                          ACCEPT             DENY                                          ${logging.path}/web_info.log                               %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n             UTF-8                                                     ${logging.path}/web-info-%d{yyyy-MM-dd}.%i.log                              100MB                                       30                                        info             ACCEPT             DENY                                          ${logging.path}/web_warn.log                               %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n             UTF-8                                         ${logging.path}/web-warn-%d{yyyy-MM-dd}.%i.log                              100MB                                       30                                        warn             ACCEPT             DENY                                          ${logging.path}/web_error.log                               %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n             UTF-8                                         ${logging.path}/web-error-%d{yyyy-MM-dd}.%i.log                              100MB                                       30                                        ERROR             ACCEPT             DENY                                                                                                         

 在上面的配置文档中,如果我们打印一个info级别的日志,执行的是以下的策略

在上面的配置文件中发现fileNamePattern中是%d{yyyy-MM-dd},也就是说maxHistory的数量是30,单位是天。

当日切时,会执行TimeBasedRollingPolicy里的rollover方法。

++TimeBasedRollingPolicy.rollover()

 ++TimeBasedArchiveRemover.cleanAsynchronously(Date now)

  ++ArhiveRemoverRunnable.run()

    ++ArhiveRemoverRunnable.clean(Date now)

通过rollover会执行ArhiveRemoverRunnable中的clean方法

public void clean(Date now) {         long nowInMillis = now.getTime();         //获取要删除的时间段         int periodsElapsed = this.computeElapsedPeriodsSinceLastClean(nowInMillis);         this.lastHeartBeat = nowInMillis;         if (periodsElapsed > 1) {             this.addInfo("Multiple periods, i.e. " + periodsElapsed + " periods, seem to have elapsed. This is expected at application start.");         }         //循环删除日志文件         for(int i = 0; i < periodsElapsed; ++i) {             //获取开端,getPeriodOffsetForDeletionTarget返回的值是yaml             //配置文件配置的maxHistory - 1             //periodsElapsed值是32,那么offset的值是-30-1到-30-1-31即-31到-62             int offset = this.getPeriodOffsetForDeletionTarget() - i;             //dateOfPeriodToClean返回的是当前时间31天到62天前的数据             Date dateOfPeriodToClean = this.rc.getEndOfNextNthPeriod(now, offset);             //执行删除动作             this.cleanPeriod(dateOfPeriodToClean);         }      }

分析computeElapsedPeriodsSinceLastClean该方法会计算删除日期范围。

int computeElapsedPeriodsSinceLastClean(long nowInMillis) {         long periodsElapsed = 0L;         if (this.lastHeartBeat == -1L) {             this.addInfo("first clean up after appender initialization");             periodsElapsed = this.rc.periodBarriersCrossed(nowInMillis, nowInMillis + 2764800000L);             periodsElapsed = Math.min(periodsElapsed, 336L);         } else {             periodsElapsed = this.rc.periodBarriersCrossed(this.lastHeartBeat, nowInMillis);         }          return (int)periodsElapsed;     }

在上面的代码中可以看到 periodBarriersCrossed方法计算时间段,该方法有两个入参分别是start和end,上面的代码中可以看到这两个入参传入的值是nowInMillis和nowInMillis + 2764800000L。

在periodBarriersCrossed方法中可以看到diff = endFloored - startFloored;而endFloored和startFloored生成使用的是同一个方法同一套规则,两者的差异只和入参有关。diff计算出的差值还是2764800000L,如果logback-spring.yaml文件里配置的单位是日,进入的是以下代码里

case TOP_OF_DAY:                 return diff / 86400000L;

 这段逻辑。2764800000L/86400000L的到的值是32。

   public long periodBarriersCrossed(long start, long end) {         if (start > end) {             throw new IllegalArgumentException("Start cannot come before end");         } else {             long startFloored = this.getStartOfCurrentPeriodWithGMTOffsetCorrection(start, this.getTimeZone());             long endFloored = this.getStartOfCurrentPeriodWithGMTOffsetCorrection(end, this.getTimeZone());             long diff = endFloored - startFloored;             switch(this.periodicityType) {             case TOP_OF_HOUR:                 return (long)((int)diff) / 3600000L;             case TOP_OF_DAY:                 return diff / 86400000L;             case TOP_OF_WEEK:                 return diff / 604800000L;             case TOP_OF_MILLISECOND:                 return diff;             case TOP_OF_SECOND:                 return diff / 1000L;             case TOP_OF_MINUTE:                 return diff / 60000L;             case HALF_DAY:             default:                 throw new IllegalStateException("Unknown periodicity type.");             case TOP_OF_MONTH:                 return (long)diffInMonths(start, end);             }         }     }

  periodsElapsed = Math.min(periodsElapsed, 336L);取periodsElapsed和336两者之间的最小值。

最终periodBarriersCrossed返回值为32即时间间隔为32天。

继续分析clean方中cleanPeriod方法,源码如下,该方法找到传入日期文件夹中的文件列表的执行删除动作 。

    public void cleanPeriod(Date dateOfPeriodToClean) {         //获取要删除日期文件夹下的文件列表         File[] matchingFileArray = this.getFilesInPeriod(dateOfPeriodToClean);         File[] arr$ = matchingFileArray;         int len$ = matchingFileArray.length;         //循环删除日期文件夹下的日志文件         for(int i$ = 0; i$ < len$; ++i$) {             File f = arr$[i$];             this.addInfo("deleting " + f);             //删除文件             f.delete();         }          if (this.parentClean && matchingFileArray.length > 0) {             File parentDir = this.getParentDir(matchingFileArray[0]);             this.removeFolderIfEmpty(parentDir);         }      }

相关内容

热门资讯

一分钟内幕!科乐吉林麻将系统发... 一分钟内幕!科乐吉林麻将系统发牌规律,福建大玩家确实真的是有挂,技巧教程(有挂ai代打);所有人都在...
一分钟揭秘!微扑克辅助软件(透... 一分钟揭秘!微扑克辅助软件(透视辅助)确实是有挂(2024已更新)(哔哩哔哩);1、用户打开应用后不...
五分钟发现!广东雀神麻雀怎么赢... 五分钟发现!广东雀神麻雀怎么赢,朋朋棋牌都是是真的有挂,高科技教程(有挂方法)1、广东雀神麻雀怎么赢...
每日必看!人皇大厅吗(透明挂)... 每日必看!人皇大厅吗(透明挂)好像存在有挂(2026已更新)(哔哩哔哩);人皇大厅吗辅助器中分为三种...
重大科普!新华棋牌有挂吗(透视... 重大科普!新华棋牌有挂吗(透视)一直是有挂(2021已更新)(哔哩哔哩)1、完成新华棋牌有挂吗的残局...
二分钟内幕!微信小程序途游辅助... 二分钟内幕!微信小程序途游辅助器,掌中乐游戏中心其实存在有挂,微扑克教程(有挂规律)二分钟内幕!微信...
科技揭秘!jj斗地主系统控牌吗... 科技揭秘!jj斗地主系统控牌吗(透视)本来真的是有挂(2025已更新)(哔哩哔哩)1、科技揭秘!jj...
1分钟普及!哈灵麻将攻略小,微... 1分钟普及!哈灵麻将攻略小,微信小程序十三张好像存在有挂,规律教程(有挂技巧)哈灵麻将攻略小是一种具...
9分钟教程!科乐麻将有挂吗,传... 9分钟教程!科乐麻将有挂吗,传送屋高防版辅助(总是存在有挂)1、完成传送屋高防版辅助透视辅助安装,帮...
每日必看教程!兴动游戏辅助器下... 每日必看教程!兴动游戏辅助器下载(辅助)真是真的有挂(2025已更新)(哔哩哔哩)1、打开软件启动之...