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],经过配置可有以下变化。

    (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>