Logback日志输出配置和使用-要点攻略

发布时间 2023-04-27 20:08:10作者: ycr_day_day_up

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个文件。

即:

image-20230129213815100
  • 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个文件。示例结果为:

    image-20230129221935535

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