<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<configuration debug="true" scan="true" scanPeriod="30 seconds">
<!--定义日志文件的存储地址 -->
<Property name="logDir" value="./logs" />
<Property name="histLogDir" value="./logs/hist"/>
<Property name="splitSize" value="1MB" />
<!--appender01 控制台日志, 控制台输出 -->
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%logger:显示类名 %msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{10} %msg%n</pattern>
</encoder>
</appender>
<!--appender02 日志输出,非滚动,每次新日志均追加到同一文件 -->
<appender name="ALLLog" class="ch.qos.logback.core.FileAppender">
<file>${logDir}/allinone.log</file>
<append>true</append>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>error</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} %msg%n</pattern>
</encoder>
</appender>
<!--appender03 根据大小和时间策略的滚动日志输出,Error及以上级别输出 -->
<appender name="RollingFileError" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>error</level>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名,日期必须加,如果单天需要生成多个日志,则需要添加%i-->
<FileNamePattern>${logDir}/error.%d{yyyy-MM-dd}-%i.log</FileNamePattern>
<!--历史日志文件保留天数,不包含当日日志,且需结合cleanHistoryOnStart字段使用-->
<!--可以按“文件数量、小时、天、月、年”等策略实现文件保留 -->
<MaxHistory>3</MaxHistory>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
<!-- 单天单个日志最大size -->
<maxFileSize>1MB</maxFileSize>
<!--仅针对当天的日志进行总size控制,日志名中的“i”保留最后数值 -->
<totalSizeCap>10MB</totalSizeCap>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%logger:显示类名 %msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} %msg%n</pattern>
</encoder>
<!--日志文件最大的大小-->
</appender>
<!--appender04 根据时间滚动策略的日志输出,Info及以上级别输出 -->
<appender name="RollingFileInfo" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>info</level>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名,不支持%i-->
<FileNamePattern>${logDir}/info.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>2</MaxHistory>
<totalSizeCap>1MB</totalSizeCap>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%logger:显示类名 %msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} %msg%n</pattern>
</encoder>
</appender>
<!--appender05 固定窗口滚动策略的日志输出,Debug及以上级别输出 -->
<appender name="RollingFileDebug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<file>${logDir}/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${logDir}/debug.%i.log</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%logger:显示类名 %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>
<logger name="com.yangchr.sb002.entity" level="DEBUG" additivity="false">
<appender-ref ref="Console" />
<appender-ref ref="RollingFileError" />
<appender-ref ref="RollingFileInfo" />
<appender-ref ref="RollingFileDebug" />
</logger>
<!-- 日志输出级别 -->
<root level="TRACE">
<appender-ref ref="Console" />
<appender-ref ref="RollingFileError" />
<appender-ref ref="RollingFileInfo" />
<appender-ref ref="RollingFileDebug" />
</root>
</configuration>
专题一、PatternLayout格式说明
对PatternLayout中,pattern属性做出参数说明。PatternLayout将决定每一行日志打出的内容和格式,对于让日志清晰可读有着重要作用。在实际使用中,应注意日志行打印内容的长度和内容全面性的关系,适当控制长度,对于日志的可读性,有很大的帮助。
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
(a)%d %date,输出日期和时间,{}中可以格式化,一般配置为yyyy-MM-dd HH:mm:ss.SSS即可。
(b)%t %thread,输出产生日志的线程名。若要限制线程编号的字符长度,可在%和t之间做位数限制。例如正常输出是[main],经过配置可有以下变化。
(c)%level %p %le 输出日志的级别(priority),这个是一定要有的。%-5level的写法,表示最少输出5个字符,多了不限,不足5个右补空格。再次观察从高到低ERROR、WARN、INFO、DEBUG、TRACE五个级别,不是4个字符就是5个,这样就使得在日志级别这里又可以做到对齐了。
(d)%logger %lo 输出日志的类名
综上,具体类名永远不会缩写,然后根据%logger中指定的字符串长度,包名从第一个开始进行首字母缩写,直到所有包名完成缩写。
(e)%msg %message %m 输出的日志信息
(f)%n 换行符
(g)%F %file 输出发出日志请求的 Java 源文件的名字。尽量避免使用,除非执行速度不造成任何问题。
(h)%L 输出执行日志请求的行号。尽量避免使用,除非执行速度不造成任何问题。
(i)%replace(p ){r, t} p 为日志内容,r 是正则表达式,将p 中符合r 的内容替换为t 。
例如, "%replace(%msg){'\s', ''}"
专题二、Rollover策略
RollingFileAppender扩展了FileAppender的功能,可以滚动生成历史日志。
比如:RollingFileAppender类负责将日志输出到 log.txt 文件,在满足了特定的条件之后,将日志输出到log1.txt文件。
与RollingFileAppender交互的两个重要的子组件 :
RollingPolicy:执行日志滚动的具体操作,比如文件移动、重命名。
TriggeringPolicy:确定是否以及何时触发日志的滚动策略。
也就是说,RollingPolicy负责what,TriggeringPolicy负责when。
为了发挥作用,RollingFileAppender必须同时设置RollingPolicy和TriggeringPolicy,如果RollingPolicy实现了TriggeringPolicy接口,则仅需配置RollingPolicy。
TimeBasedRollingPolicy基于时间滚动策略
因为TimeBasedRollingPolicy实现了TriggeringPolicy接口,所以我们使用的时候只需要配置rollingPolicy节点,不需要配置TriggeringPolicy。
TimeBasedRollingPolicy的参数如下:
fileNamePattern:定义历史日志的名称、保存格式(txt、gz)
maxHistory:设置历史日志的保存时间(超时的删除)默认设置为0,表示不会删除。
totalSizeCap :历史日志的总大小。当文件超过总大小上限时,最早的历史日志将被异步删除。默认设置为0,表示历史日志大小无限制。
cleanHistoryOnStart:如果设置为 true,则在appender运行时会将历史日志删除。默认设置为 false。
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${logDir}/debug.%d{yyyy-MM-dd}.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日志将会保留30天,超过30天的文件将删除。同时,下面还设置了triggeringPolicy,MaxFileSize为10MB,文件大小达到10MB的时候也会触发滚动,比如在测试阶段这个error日志特别庞大,一天之内就能达到10MB,那么因为这个设置就会提前触发滚动。但是由于FileNamePattern中缺少 %i 参数,滚动之后的文件名与当前日志文件名无法进行区分,因此笔者认为这里的triggeringPolicy是不生效的。
maxHistory的值与fileNamePattern设置的格式有关,如果保存格式为yyyyMMddHHmm,那maxHistory的时间单位就是分钟,如果保存格式为yyyyMMdd,那maxHistory的时间单位就是天。但是,有些应用程序的生存时间太短,短到不足以触发日志滚动策略。对于这样的应用程序,历史日志的删除可能永远不会有执行的机会。可以通过将 cleanHistoryOnStart 设置为 true,在appender启动时删除历史日志。
TimeBasedRollingPolicy支持自动文件压缩。如果 fileNamePattern 的参数以.gz 或 .zip 结尾,则会启用此功能。
日志的滚动不是由时钟触发,而是需要日志写入动作来触发。假设2022年12月18日 fileNamePattern 设置为yyyy-MM-dd 日志按天滚动,则滚动会发生在0点后第一个日志写入时,比如在00:23:47写入第一个event,那么日志会在 2022 年 12 月 19日 00:23:47进行滚动,而不是2022 年 12月 19日 00:00:00。
SizeAndTimeBasedRollingPolicy基于时间和文件大小的滚动策略
前面的TimeBasedRollingPolicy已经可以限制历史日志文件的时间和总文件的大小,如果需要限制每个文件的大小可以用SizeAndTimeBasedRollingPolicy
官网没有说SizeAndTimeBasedRollingPolicy是否实现了TriggeringPolicy接口,但从官网给出例子中看并没有配置TriggeringPolicy,况且TriggeringPolicy仅支持一个参数MaxFileSize,很明显基于时间和文件大小的RollingPolicy自己就可以触发,因此无需配置TriggeringPolicy。
官网示例:
<configuration>
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>mylog.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>mylog.%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<!-- 每个文件最大 100MB, 最多保存60天 ,总的大小不超过20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="ROLLING" />
</root>
</configuration>
在这里%i 和%d 标记都是必需的参数,如果单个文件在当前时间段结束之前达到 maxFileSize,i 就会以从 0 开始的递增索引对日志文件进行归档如下:
mylog.2022-12-19.0.txt、mylog.2022-12-19.1.txt、mylog.2022-12-19.2.txt......
在这里,如果应用程序意外停止或重新启动时,仍会在程序停止时的日志文件中继续打印日志,也就是目录中索引号i最大的文件。
专题三、输出异步日志AsyncAppender
logback的异步日志输出很简单,在原有配置基础上添加一个基于异步写日志的appender,即AsyncAppender,并指向原先配置的appender即可。
AsyncAppender并不处理日志,只是将日志缓冲到一个BlockingQueue里面去,并在内部创建一个工作线程从队列头部获取日志,之后将获取的日志循环记录到附加的其他appender上去,从而达到不阻塞主线程的效果。因此AsynAppender仅仅充当事件转发器,必须引用另一个appender来做事。
默认情况下,AsyncAppender会在队列满80%的情况下删除TRACE、DEBUG和INFO级别的事件。这种策略以事件损失为代价,对性能却有很大的提升。
<!--异步输出 appender-->
<appender name="ASYNC-TRACE" class="ch.qos.logback.classic.AsyncAppender">
<!-- 队列剩余容量小于discardingThreshold,则会丢弃TRACT、DEBUG、INFO级别的日志;默认值-1,为queueSize的20%;0表示不丢失日志 -->
<discardingThreshold>-1</discardingThreshold>
<!-- 队列满了,是否阻塞,默认为false;如果配置为true,则队列满了就丢弃日志; -->
<neverBlock>true</neverBlock>
<!-- 队列的最大容量,该值会影响性能.默认值为256 -->
<queueSize>256</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="TraceFile"/>
</appender>
<appender name="ASYNC-ERROR" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<neverBlock>true</neverBlock>
<queueSize>256</queueSize>
<appender-ref ref="ErrorFile"/>
</appender>
<appender name="ASYNC-INFO" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<neverBlock>true</neverBlock>
<queueSize>256</queueSize>
<appender-ref ref="InfoFile"/>
</appender>