问题描述
线上查看Mqtt消息服务项目时,发现服务器中系统的日志不能按天生成日志文件,全部的日志信息都打印到了启动运行项目的那一天中了且此日志文件越来越大,只有重启项目之后才会生成当天的日志文件。
回想当时开发的时候为什么没有发现?
1. 开发的时候,基本每天都会重启,且日志正常;
2. 部署的时候,打包的时候日志文件夹也会打包进入,当时启动后看了看正常,就所以然了。
分析原因
这个Mqtt消息服务项目使用SpringBoot + Logback框架,查看其配置文件,发现**<font color='red'> 策略组合</font>** 使用问题。
以下是当时项目中的有问题的配置文件
<configuration debug="false"> <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径--> <property name="LOG_HOME" value="./logs" /> <!--配置CONSOLE控制台文件输出项--> <appender name="CONSOLELOG" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS } [%thread] %-5level %logger{50} - %msg %n</pattern> </encoder> </appender> <!--配置INFO文件输出项--> <appender name="DAYINFOLOG" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <!--过滤error日志--> <level>ERROR</level> <onMatch>DENY</onMatch> <onMismatch>ACCEPT</onMismatch> </filter> <!--配置滚动策略--> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 按照每天生成日志文件 --> <!--日志文件输出的文件名--> <FileNamePattern>${LOG_HOME}/info/%d{yyyy-MM-dd}-info.log</FileNamePattern> <!--日志文件保留天数--> <MaxHistory>30</MaxHistory> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> <!--日志文件最大的大小--> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>10MB</MaxFileSize> </triggeringPolicy> </appender> <!--配置ERROR文件输出项--> <appender name="DAYERRORLOG" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 比较日志记录请求的Level值和ThresholdFilter中配置的Level值 当日志记录请求的Level值小于ThresholdFilter中配置的Level值,日志记录请求被判定为无效 --> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level> </filter> <!--配置滚动策略--> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 按照每天生成日志文件 --> <!--日志文件输出的文件名--> <FileNamePattern>${LOG_HOME}/error/%d{yyyy-MM-dd}-error.log</FileNamePattern> <!--日志文件保留天数--> <MaxHistory>30</MaxHistory> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg %n</pattern> </encoder> <!--日志文件最大的大小--> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>10MB</MaxFileSize> </triggeringPolicy> </appender> <!-- 日志输出级别 --> <root level="INFO"> <appender-ref ref="CONSOLELOG" /> <appender-ref ref="DAYINFOLOG"/> <appender-ref ref="DAYERRORLOG"/> </root> </configuration>
配置中使用了基于时间的滚动切割策略 **TimeBasedRollingPolicy**,每天凌晨0点自动生成新的日志文件。但是可以注意到里面包含了一段 triggeringPolicy触发策略,即最大单个文件超过 10MB 自动新成新日志文件, TimeBasedRollingPolicy 是基于时间的,不能和其他策略一起组合使用。
这个配置文件包含了两个策略:
1. 时间维度;
2. 文件大小维度;
解决方案
方案一:去掉组合策略 <font color='red'>**triggeringPolicy** </font>
<appender name="DAYINFOLOG" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <!--过滤error日志--> <level>ERROR</level> <onMatch>DENY</onMatch> <onMismatch>ACCEPT</onMismatch> </filter> <!--配置滚动策略--> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 按照每天生成日志文件 --> <!--日志文件输出的文件名--> <FileNamePattern>${LOG_HOME}/info/%d{yyyy-MM-dd}-info.log</FileNamePattern> <!--日志文件保留天数--> <MaxHistory>30</MaxHistory> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> // 错误输出日志同理,不再只是展示 ``` 方案二:使用时间和大小组合策略 <font color='red'> **SizeAndTimeBasedRollingPolicy **</font> ```xml <appender name="DAYINFOLOG" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <!--过滤error日志--> <level>ERROR</level> <onMatch>DENY</onMatch> <onMismatch>ACCEPT</onMismatch> </filter> <!--配置滚动策略--> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!-- 按照每天生成日志文件 --> <!--日志文件输出的文件名--> <FileNamePattern>${LOG_HOME}/info/%d{yyyy-MM-dd}-info-%i.log</FileNamePattern> <!--日志文件保留天数--> <MaxHistory>30</MaxHistory> <maxFileSize>20MB</maxFileSize> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender>
注意事项
<FileNamePattern>${LOG_HOME}/info/%d{yyyy-MM-dd}-info-%i.log</FileNamePattern>
注意后面的<font color='red'> `%i`</font>是必须要加上去的,是单个日志文件超大小后的切割序号,以便我们分析处理。
使用以上两种解决方案都可以解决Logback不能按天切割生成日志文件的问题,