• logback服务器日志删除原理分析


        查看以下的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日)的日志。

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

    以下是日志配置文件:

    1. "1.0" encoding="UTF-8"?>
    2. <configuration scan="true" scanPeriod="10 seconds">
    3. <contextName>MyContextNamecontextName>
    4. <springProperty name="serverName" source="logging.file.name" defaultValue="MyServerName"/>
    5. <springProperty name="logging.path" source="logging.file.path" defaultValue="././logs/"/>
    6. <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
    7. <conversionRule conversionWord="wex"
    8. converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
    9. <conversionRule conversionWord="wEx"
    10. converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
    11. <property name="CONSOLE_LOG_PATTERN"
    12. value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
    13. <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    14. <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
    15. <level>debuglevel>
    16. filter>
    17. <encoder>
    18. <Pattern>${CONSOLE_LOG_PATTERN}Pattern>
    19. <charset>UTF-8charset>
    20. encoder>
    21. appender>
    22. <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    23. <file>${logging.path}/web_debug.logfile>
    24. <encoder>
    25. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
    26. <charset>UTF-8charset>
    27. encoder>
    28. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    29. <fileNamePattern>${logging.path}/web-debug-%d{yyyy-MM-dd}.%i.logfileNamePattern>
    30. <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
    31. <maxFileSize>100MBmaxFileSize>
    32. timeBasedFileNamingAndTriggeringPolicy>
    33. <maxHistory>30maxHistory>
    34. rollingPolicy>
    35. <filter class="ch.qos.logback.classic.filter.LevelFilter">
    36. <level>debuglevel>
    37. <onMatch>ACCEPTonMatch>
    38. <onMismatch>DENYonMismatch>
    39. filter>
    40. appender>
    41. <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    42. <file>${logging.path}/web_info.logfile>
    43. <encoder>
    44. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
    45. <charset>UTF-8charset>
    46. encoder>
    47. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    48. <fileNamePattern>${logging.path}/web-info-%d{yyyy-MM-dd}.%i.logfileNamePattern>
    49. <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
    50. <maxFileSize>100MBmaxFileSize>
    51. timeBasedFileNamingAndTriggeringPolicy>
    52. <maxHistory>30maxHistory>
    53. rollingPolicy>
    54. <filter class="ch.qos.logback.classic.filter.LevelFilter">
    55. <level>infolevel>
    56. <onMatch>ACCEPTonMatch>
    57. <onMismatch>DENYonMismatch>
    58. filter>
    59. appender>
    60. <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    61. <file>${logging.path}/web_warn.logfile>
    62. <encoder>
    63. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
    64. <charset>UTF-8charset>
    65. encoder>
    66. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    67. <fileNamePattern>${logging.path}/web-warn-%d{yyyy-MM-dd}.%i.logfileNamePattern>
    68. <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
    69. <maxFileSize>100MBmaxFileSize>
    70. timeBasedFileNamingAndTriggeringPolicy>
    71. <maxHistory>30maxHistory>
    72. rollingPolicy>
    73. <filter class="ch.qos.logback.classic.filter.LevelFilter">
    74. <level>warnlevel>
    75. <onMatch>ACCEPTonMatch>
    76. <onMismatch>DENYonMismatch>
    77. filter>
    78. appender>
    79. <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    80. <file>${logging.path}/web_error.logfile>
    81. <encoder>
    82. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
    83. <charset>UTF-8charset>
    84. encoder>
    85. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    86. <fileNamePattern>${logging.path}/web-error-%d{yyyy-MM-dd}.%i.logfileNamePattern>
    87. <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
    88. <maxFileSize>100MBmaxFileSize>
    89. timeBasedFileNamingAndTriggeringPolicy>
    90. <maxHistory>30maxHistory>
    91. rollingPolicy>
    92. <filter class="ch.qos.logback.classic.filter.LevelFilter">
    93. <level>ERRORlevel>
    94. <onMatch>ACCEPTonMatch>
    95. <onMismatch>DENYonMismatch>
    96. filter>
    97. appender>
    98. <springProfile name="dev">
    99. <logger name="com.myClass.controller" level="debug"/>
    100. springProfile>
    101. <root level="info">
    102. <appender-ref ref="CONSOLE"/>
    103. <appender-ref ref="DEBUG_FILE"/>
    104. <appender-ref ref="INFO_FILE"/>
    105. <appender-ref ref="WARN_FILE"/>
    106. <appender-ref ref="ERROR_FILE"/>
    107. root>
    108. configuration>

     在上面的配置文档中,如果我们打印一个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方法

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

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

    1. int computeElapsedPeriodsSinceLastClean(long nowInMillis) {
    2. long periodsElapsed = 0L;
    3. if (this.lastHeartBeat == -1L) {
    4. this.addInfo("first clean up after appender initialization");
    5. periodsElapsed = this.rc.periodBarriersCrossed(nowInMillis, nowInMillis + 2764800000L);
    6. periodsElapsed = Math.min(periodsElapsed, 336L);
    7. } else {
    8. periodsElapsed = this.rc.periodBarriersCrossed(this.lastHeartBeat, nowInMillis);
    9. }
    10. return (int)periodsElapsed;
    11. }

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

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

    1. case TOP_OF_DAY:
    2. return diff / 86400000L;

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

    1. public long periodBarriersCrossed(long start, long end) {
    2. if (start > end) {
    3. throw new IllegalArgumentException("Start cannot come before end");
    4. } else {
    5. long startFloored = this.getStartOfCurrentPeriodWithGMTOffsetCorrection(start, this.getTimeZone());
    6. long endFloored = this.getStartOfCurrentPeriodWithGMTOffsetCorrection(end, this.getTimeZone());
    7. long diff = endFloored - startFloored;
    8. switch(this.periodicityType) {
    9. case TOP_OF_HOUR:
    10. return (long)((int)diff) / 3600000L;
    11. case TOP_OF_DAY:
    12. return diff / 86400000L;
    13. case TOP_OF_WEEK:
    14. return diff / 604800000L;
    15. case TOP_OF_MILLISECOND:
    16. return diff;
    17. case TOP_OF_SECOND:
    18. return diff / 1000L;
    19. case TOP_OF_MINUTE:
    20. return diff / 60000L;
    21. case HALF_DAY:
    22. default:
    23. throw new IllegalStateException("Unknown periodicity type.");
    24. case TOP_OF_MONTH:
    25. return (long)diffInMonths(start, end);
    26. }
    27. }
    28. }

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

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

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

    1. public void cleanPeriod(Date dateOfPeriodToClean) {
    2. //获取要删除日期文件夹下的文件列表
    3. File[] matchingFileArray = this.getFilesInPeriod(dateOfPeriodToClean);
    4. File[] arr$ = matchingFileArray;
    5. int len$ = matchingFileArray.length;
    6. //循环删除日期文件夹下的日志文件
    7. for(int i$ = 0; i$ < len$; ++i$) {
    8. File f = arr$[i$];
    9. this.addInfo("deleting " + f);
    10. //删除文件
    11. f.delete();
    12. }
    13. if (this.parentClean && matchingFileArray.length > 0) {
    14. File parentDir = this.getParentDir(matchingFileArray[0]);
    15. this.removeFolderIfEmpty(parentDir);
    16. }
    17. }

  • 相关阅读:
    代码随想录day59
    【千律】OpenCV基础:Hough圆检测
    99 # mongo 的基本安装和配置
    从抽象类和普通类的区别中体会设计模式
    键鼠自动化2.0树形结构讲解
    打造千万级流量秒杀第十八课 热更新:如何解决程序升级中的稳定性问题?
    01 【介绍 使用步骤 引入方式 基础配置】
    ElementUI自定义主题
    用于准确量化颅面对称性和面部生长的 3D 头影测量方案(Matlab代码实现)
    BI工具:让数据分析井然有序一望而知
  • 原文地址:https://blog.csdn.net/xl649138628/article/details/133650908