Logback日志输出配置和使用-要点攻略
Logback是由log4j创始人设计的另一个开源日志组件,比log4j功能更强大,效率更高。官方网站:http://logback.qos.ch/documentation.html。
本文较为详细地讲述logback的日志输出使用原理、如何配置,并结合具体的代码,给出程序调用的方法。为了讲清原理,本文从log4j的日志级别开讲,然后讲述主配置文件log4j2.xml的配置方法、程序调用方法,并给出一个比较完整的log4j2配置文件。
一、Logback的级别解读
Logback共定义了8个级别的log(除去OFF和ALL,可以说分为6个级别),优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。目前log4j官方推荐的共四个级别:ERROR、WARN、INFO、DEBUG。
OFF-第8级,最高等级,虚拟级别,用于关闭所有日志记录。
FATAL-第7级,不可用的致命级别,已被slf4j弃用,官方解释是和ERROR没有绝对的界限。
ERROR-第6级,可用的错误级别,很常用,推荐级别,用于捕获错误事件。
WARN-第5级,可用的告警级别,用得相对较少但位置很关键,表示潜在的可能错误。
INFO-第4级,可用的信息级别,最常用,推荐级别,用于打出程序的关键信息、阶段性的历程碑信息。
DEBUG-第3级,可用的调试级别,详尽的、可用于程序调试的级别,看日志类似看代码的执行过程。
TRACE-第2级,可用的追踪级别,但一般不建议使用,用于极为详尽的step-by-step日志追踪。
ALL-第1级,最低等级,虚拟级别,用于打开所有日志记录。
二、Logback的配置文件
1 配置文件原理
(1) 配置文件查找顺序
Logback查找配置文件的先后顺序是:
1.logback-test.xml;
2.logback.groovy;
3.logback.xml;
如果上述三个配置文件都没有找到,则使用默认配置打印到控制台。
附上Logback查找配置文件的过程:
20:35:42,642 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
20:35:42,644 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
20:35:42,648 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [file:/F:/Study/workspace/logBackTest/bin/logback.xml]
Logback优先读取测试环境配置,后读取生产环境配置。此外,相比于XML,Groovy风格的配置文件更加直观,当前已有工具支持自动把logback.xml文件迁移至logback.groovy,但当前使用较少,因此本文的讲述,都基于logback.xml。
Logback配置文件中所有的element、属性值、属性名、类名、文件、目录名、PatternLayout的字符串等均区分大小写。
(2) 配置文件基本结构
Logback.xml的基本结构如下所示:
根element为configuration,一个configuration至少要包含一个root和一个appender,logger为可选项,如有需要appender和logger可配置多个。
......
<configuration debug="false" scan="true" scanPeriod="1 seconds">
......
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter ......>
<rollingPolicy
<encoder ......>
......
</appender>
......
<logger name=......>
.....
</loggers>
......
<root ......>
......
</root>
</configuration>
(3)关于对appender和logger的理解
logger用来收集日志,appender用来输出日志,此外root是一种特殊的logger,所以整体来说,configuration里面配置的其实就是两类内容:appender和logger。
关于对其关系的理解,笔者看到了一个非常好的比喻,即logger是会写字的人,appender是文字的输出或者展现方式,可以理解为黑板、本子等等。logger和appender共同完成的就是:一个人在收集到日志后,根据需要可以将字写在黑板上,也可以将字写在本子上,供给需要的人看。
(4) logback.xml的配置优势
logback.xml配置起来方便灵活,比如某级别日志独立输出、异步日志等,通过调用不同的class即可实现。
2 起始和Configuration总段落
(1) xml起始
起始段落为固定的写法,注意encoding写为UTF-8,!DOCTYPE用以声明文件类型,后续文件类型不区分大小写。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
(2) Configuration根节点属性
Configuration共有三个属性:
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
scan:配置是否对配置文件修改进行检测,默认值为true,即:配置文件如果发生改变,将会被重新加载。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
<configuration debug="false" scan="true" scanPeriod="30 seconds">
(3) Properties段落
这个段落主要用于配置Logback的属性,更有用的是配置一些变量,用在后面的appenders段落和loggers段落用${VARIABLE}引用。
这里配置日志存放目录、历史日志存放目录、日志文件切换大小这3个变量。
<Property name="logDir" value="./logs" />
<Property name="histLogDir" value="./logs/hist"/>
<Property name="splitSize" value="1MB" />
3 appenders段落
本段落可包括多个appender,每个appender均代表一种日志输出格式。
每个appender都有两个属性name和class,name用来给当前appender命名,以便logger使用。class用来指定输出策略,常用就是控制台输出策略和文件输出策略,而文件输出策略包括基础的FileAppender,以及基于时间等维度的RollingFileAppender。
如:控制台输出
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
如:滚动日志输出
<appender name="ErrorFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
此外,appender主要包括以下子节点:
- filter 过滤器,可以自定义拦截器也可以用系统一些定义好的拦截器
- file &append fileappender策略开启时,用来指明日志文件的输出位置,可以是绝对路径也可以是相对路径
- encoder 指定具体输出的日志格式
- rollingPolicy 日志滚动策略
- triggerPolicy 触发
我们将逐一介绍上述各子节点。
(1)filter
任一输出策略类型的appender均可使用filter,主要分两种,一是级别过滤器,根据日志级别进行输出,一是阈值过滤器,与log4j类似,用以过滤掉低于指定级别的日志。相较log4j,Logback对不同级别日志的过滤方式更为丰富灵活。下面我们来看两种过滤器的范例。
- 级别过滤器
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
执行结果:
2022-12-17 15:50:57.635 [main] INFO com.yangchr.sb002.entity.logBackTest 日志输出,INFO 日志---256,托尼
在级别过滤器中,共有三个子element,level用以设置过滤级别,onMatch和onMismatch用以配置满足和不满足过滤条件时的操作,包括ACCEPT、DENY、NEUTRAL。在上面的例子中,onMatch="ACCEPT" onMismatch="DENY"表示,高于等于DEBUG级别的日志给与输出,非INFO级别的日志不予输出。
从这个例子可以看出,Logback在实现单一级别日志输出的时候,相较log4j来说,是非常方便的,仅通过调用级别过滤器即实现了姊妹篇《log4j2日志输出配置和使用-要点攻略》中的专题三内容。
- 阈值过滤器
阈值处理器中仅一个子element,即level。当日志级别等于或高于临界值level时,过滤器返回NEUTRAL;当日志级别低于临界值时,日志返回DENY。
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>info</level>
</filter>
输出结果为:
2022-12-17 16:28:58.185 [main] ERROR com.yangchr.sb002.entity.logBackTest 日志输出,ERROR日志---256,托尼
2022-12-17 16:28:58.185 [main] WARN com.yangchr.sb002.entity.logBackTest 日志输出,WARN 日志---256,托尼
2022-12-17 16:28:58.185 [main] INFO com.yangchr.sb002.entity.logBackTest 日志输出,INFO 日志---256,托尼
从上面的例子看出,Logback通过阈值过滤器实现了log4j2中的ThresholdFilter 功能。
(2)file & append
当日志输出策略为fileappend时,用到file & append两个子element。其中file用以指定待写入的文件名,可带绝对或相对路径。append为true时,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>./FileLog/File.log</file>
<append>true</append>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} %msg%n</pattern>
</encoder>
</appender>
(3)rollingPolicy
Logback共有三种滚动策略,但与log4j略有不同。Logback中的基础滚动策略不支持onlySizeBased滚动,该策略可通过固定窗口滚动策略+按大小触发滚动间接实现。
特别需说明,logback的rolling不是时间驱动的,而是事件驱动,如果配置按天,不一定是0点0分会触发切分,需要产生日志才会触发是否切分的判断。总结来说,虽迟但到。
三种滚动策略简介如下,调用时仅需指定相对应的class、并配置相应的子element即可。
- TimeBasedRoolingPolicy 基于时间滚动策略
本策略共有以下三个子element:
fileNamePattern:必需的element,可以用来设置指定时间的日志归档。
maxHistory:可选element,控制保留的归档文件的最大数量,超出数量就删除旧文件,加入设置为2的话,则除当日日志外,仅保留过去2天内的日志。
特别需要说明的是,经笔者多次尝试,本element需结合cleanHistoryOnStart一起使用。原因为由于logback的时间范围计算策略问题,maxHistory可能不生效。
totalSizeCap:(示例未予展示)可选element,用来指定日志文件的上限大小,例如设置为1MB的话,那么当日至总大小到了这个值,就会删除旧的日志
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名,不支持%i-->
<FileNamePattern>${logDir}/info.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>2</MaxHistory>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
本策略有一点需注意:即TimeBasedRollingPolicy不可与trigger policy中的SizeBasedTriggeringPolicy共同使用,该需求可通过后续要讲的SizeAndTimeBasedRollingPolicy实现。
- SizeAndTimeBasedRollingPolicy基于大小和时间的滚动策略
本策略同时基于时间和文件大小进行滚动,子element也比较简单。
<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>
上述示例中的配置表示:单个日志最大1MB,总日志最大10MB(即最多保留九或十个日志文件),日志日期最多保留过去3天。
注:每天生成的多个文件,根据大小滚动起来后,其文件名后缀的%i不断累加,在上例中仅保留最后9个文件。
即:
-
Fixed Window Rolling Policy 固定窗口的滚动策略
本策略element主要有两个:minIndex和maxIndex用以明确窗口索引的最大最小值,如这两个值分别设置为1和3,即该日志最多归档文件为3个。
本策略使用场景较少,一般结合triggeringPolicy来使用,示例如下:
<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"> <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>
本策略配合triggeringPolicy,实现了log4j中的onlySizeRollingPolicy。上述示例配置表示:单个日志文件通过triggeringPolicy超过1OMB时触发滚动,历史日志滚动最小保留1个文件,最多保留3个文件。示例结果为:
(4)encoder
Logback自0.9.19版本开始支持encoder。encoder 用于将日志信息转为字节数组,并将字节数据写入到输入流。除了个别appender已经内置了日志格式,每个appender中都必须有一个encoder,其属性class最常用的值为:
ch.qos.logback.classic.encoder.PatternLayoutEncoder
唯一一个子element即pattern。先看下述示例:
<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>
pattern格式详见后续专题一。
4 loggers段落
loggers段落负责将日志按照指定级别过滤,并输出到指定预定义好的Appender中。
在loggers段落中,包括必须有的root段和可选的logger,如果没有root端,将会默认不过滤日志并将日志输出到控制台。此外非常重要的是,root是一种特殊的logger。继续使用前面的比喻,logger可以有多个,就是有多个会写字的人。logger默认的打印级别是继承root的,如果它不想继承,想自立门户的话,就自行设置level;若不愿和root打重复日志,则需加上additivity=false,这是非常重要的配置技巧。
logger示例如下
<logger name="com.yangchr.sb002.entity" level="INFO" additivity="false">
<appender-ref ref="Console" />
<appender-ref ref="RollingFileError" />
<appender-ref ref="RollingFileInfo" />
<appender-ref ref="RollingFileDebug" />
<appender-ref ref="RollingFileYCR" />
</logger>
<!-- 日志输出级别 -->
<root level="TRACE">
<appender-ref ref="Console" />
<appender-ref ref="RollingFileError" />
<appender-ref ref="RollingFileInfo" />
<appender-ref ref="RollingFileDebug" />
</root>
三、程序调用Logback
目前流行的logback、log4j2,都是基于slf4j的实现,这里给出使用logback实现slf4j的代码和相关配置。
1 原生代码调用Logback
Logback调用共需3个jar包:
logback-classic-1.2.3.jar
logback-core-1.2.3.jar
slf4j-api-1.7.26.jar
2 代码中定义slf4j的logger
给出本攻略中一直在用的代码:
package com.yangchr.sb002.entity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class logBackTest {
private Integer empID;
private String empName;
private static final Logger logger = LoggerFactory.getLogger(logBackTest.class);
public logBackTest(Integer empID, String empName) {
this.empID = empID;
this.empName = empName;
}
@Override()
public String toString() {
return (this.empID +","+this.empName);
}
public void print(String output) {
logger.error("日志输出,ERROR日志---{}", output);
logger.warn ("日志输出,WARN 日志---{}", output);
logger.info ("日志输出,INFO 日志---{}", output);
logger.debug("日志输出,DEBUG日志---{}", output);
logger.trace("日志输出,TRACE日志---{}", output);
}
public static void main(String[] args) {
logBackTest employee = new logBackTest(256, "托尼");
employee.print(employee.toString());
}
}
四、一个完整的logback.xml
<?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],经过配置可有以下变化。
编号 | 写法 | 输出 | 说明 |
---|---|---|---|
1 | [%t] | [main] | 标准输出,输出线程完整的名字 |
2 | [%3t] | [main] | 从右算起,最少输出三个字符,多了不限,由于main是四个字符,因此全部输出 |
3 | [%8t] | [ main] | 从右算起,最少输出八个字符,多了不限,于是出现左边补空格 |
4 | [%-3t] | [main] | 从左算起,最少输出三个字符,由于main是四个字符,因此全部输出 |
5 | [%.3t] | [ain] | 从右算起,最多输出三个字符,其余被截掉 |
6 | [%.8t] | [main] | 从右算起,最多输出8个字符,其余被截掉,由于main只有四个字符,因此全部输出 |
7 | [%5.8t] | [ main] | 从右算起,最少输出5个字符,最多输出8个字符,<5则左补空格,>8则左边的截断 |
8 | [%2.3t] | [ain] | 从右算起,最少输出2个字符,最多输出3个字符,<2则左补空格,>3则左边的截断 |
9 | [%2.2t] | [in] | 从右算起,固定输出2个字符,即<=2且>=2,那么就是=2,不足2个左补空格,多余2个左边截断 |
10 | [%-2.3t] | [ain] | 从左算起,最少输出2个字符,最多输出3个字符,<2则右补空格,>3则左边的截断 |
11 | [%-2.3t] | [mai] | 从左算起,最少输出2个字符,最多输出3个字符,<2则右补空格,>3则右边的截断 |
12 | [%-2.-8t] | [main] | 从左算起,最少输出2个字符,最多输出8个字符,<2则右补空格,>8则右边的截断 |
13 | [%-8.-8t] | [main ] | 从左算起,固定输出8个字符,<8右边补空格,>8右边的截断 |
(c)%level %p %le 输出日志的级别(priority),这个是一定要有的。%-5level的写法,表示最少输出5个字符,多了不限,不足5个右补空格。再次观察从高到低ERROR、WARN、INFO、DEBUG、TRACE五个级别,不是4个字符就是5个,这样就使得在日志级别这里又可以做到对齐了。
(d)%logger %lo 输出日志的类名
编号 | 写法 | 输出 | 说明 |
---|---|---|---|
1 | %logger | com.yangchr.sb002.entity | 输出完整的类名 |
2 | %logger | entity | 只输出logger最右边点符号.之后的字符串 |
3 | %logger | c.y.s.entity | entity为6字符,已经超过5,所以类名最后一个单词原样展示,之前的包路径首字母缩写展示。最后一个单词永远不会被缩写。 |
4 | %logger | c.y.s.entity | entity为6字符,sb002为5字符,仅二者相加就超过10,因此最后输出效果与%logger{5}一致 |
5 | %logger | c.y.sb002.entity | 最多输出17个字符(符号.也算一个字符) |
6 | %logger | c.yangchr.sb002.entity | 最多输出23个字符 |
7 | %logger | com.yangchr.sb002.entity | 最多输出26个字符 |
8 | %logger | com.yangchr.sb002.entity | logger名最大占用的字符位数为50,写50通常为了类名过长时显全类名 |
综上,具体类名永远不会缩写,然后根据%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>