欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

深入剖析源码系列(第七期):详细解读Logback的运用与内核代码解析

最编程 2024-02-23 21:52:45
...

什么是logback

logback 用于日志记录,可以将日志输出到控制台、文件、数据库和邮件等,相比其它所有的日志系统,logback 更快并且更小,包含了许多独特并且有用的特性。

logback 被分成三个不同的模块:logback-core,logback-classic,logback-access。

  1. logback-core 是其它两个模块的基础。
  2. logback-classic 模块可以看作是 log4j 的一个优化版本,它天然的支持 SLF4J。
  3. logback-access 提供了 http 访问日志的功能,可以与 Servlet 容器进行整合,例如:Tomcat、Jetty。

本文将介绍以下内容,由于篇幅较长,可根据需要选择阅读:

  1. 如何使用 logback:将日志输出到控制台、文件和数据库,以及使用 JMX 配置 logback;
  2. logback 配置文件详解;
  3. logback 的源码分析。

如何使用logback

需求

  1. 使用 logback 将日志信息分别输出到控制台、文件、数据库。
  2. 使用 JMX 方式配置 logback。

工程环境

JDK:1.8.0_231
maven:3.6.1
IDE:Spring Tool Suite 4.3.2.RELEASE
mysql:5.7.28

主要步骤

  1. 搭建环境;
  2. 配置 logback 文件;
  3. 编写代码:获取 Logger 实例,并打印指定等级的日志;
  4. 测试。

创建项目

项目类型 Maven Project ,打包方式 jar。

引入依赖

logack 天然的支持 slf4j,不需要像其他日志框架一样引入适配层(如 log4j 需引入 slf4j-log4j12 )。通过后面的源码分析可知,logback 只是将适配相关代码放入了 logback-classic。

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- logback+slf4j -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.28</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.3</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
            <type>jar</type>
        </dependency>
        <!-- 输出日志到数据库时需要用到 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.17</version>
        </dependency>
        <!-- 使用数据源方式输出日志到数据库时需要用到 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.4</version>
        </dependency>
    </dependencies>

将日志输出到控制台

配置文件

配置文件放在 resources 下,文件名可以为 logback-test.xml 或 logback.xml,实际项目中可以考虑在测试环境中使用 logback-test.xml ,在生产环境中使用 logback.xml( 当然 logback 还支持使用 groovy 文件或 SPI 机制进行配置,本文暂不涉及)。

在 logback中,logger 可以看成为我们输出日志的对象,而这个对象打印日志时必须遵循 appender 中定义的输出格式和输出目的地等。注意,root logger 是一个特殊的 logger。

<configuration>
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!--定义控制台输出格式-->
        <encoder charset="utf-8">
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

另外,即使我们没有配置,logback 也会默认产生一个 root logger ,并为它配置一个 ConsoleAppender

编写测试类

为了程序的解耦,一般我们在使用日志时会采用门面模式,即通过 slf4j 或 commons-logging 来获取 Logger 对象。

以下代码中,导入的两个类 LoggerLoggerFactory都定义在 slf4j-api 中,完全不会涉及到 logback 包的类。这时,如果我们想切换 log4j 作为日志支持,只要修改 pom.xml 和日志配置文件就行,项目代码并不需要改动。源码分析部分将分析 slf4j 如何实现门面模式。

    @Test
    public void test01() {
        Logger logger = LoggerFactory.getLogger(LogbackTest.class);
        
        logger.debug("输出DEBUG级别日志");
        logger.info("输出INFO级别日志");
        logger.warn("输出WARN级别日志");
        logger.error("输出ERROR级别日志");
        
    }

注意,这里获取的 logger 不是我们配置的 root logger,而是以 cn.zzs.logback.LogbackTest 命名的 logger,它继承了祖先 root logger 的配置

测试

运行测试方法,可以看到在控制台打印如下信息:

2020-01-16 09:10:40 [main] INFO  ROOT - 输出INFO级别的日志
2020-01-16 09:10:40 [main] WARN  ROOT - 输出WARN级别的日志
2020-01-16 09:10:40 [main] ERROR ROOT - 输出ERROR级别的日志

这时我们会发现,怎么没有 debug 级别的日志?因为我们配置了日志等级为 info,小于 info 等级的日志不会被打印出来。日志等级如下:

ALL < TRACE < DEBUG < INFO < WARN < ERROR < OFF

将日志输出到滚动文件

本例子将在以上例子基础上修改。测试方法代码不需要修改,只要修改配置文件就可以了。

配置文件

前面已经讲过,appender 中定义日志的输出格式和输出目的地等,所以,要将日志输出到滚动文件,只要修改appender 就行。logback 提供了RollingFileAppender来支持打印日志到滚动文件。

以下配置中,设置了文件大小超过100M后会按指定命名格式生成新的日志文件。

<configuration>

    <!-- 定义变量 -->
    <property name="LOG_HOME" value="D:/growUp/test/log" />
    <property name="APP_NAME" value="logback-demo"/>
    
    <!-- 滚动文件输出 -->
    <appender name="FILE-ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 指定日志文件的名称 -->
        <file>${LOG_HOME}/${APP_NAME}/error.log</file>
        
        <!-- 配置追加写入 -->
        <append>true</append>    
        
           <!-- 级别过滤器 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        
        <!-- 滚动策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 滚动文件名称  -->
            <fileNamePattern>${LOG_HOME}/${APP_NAME}/notError-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。
                注意,删除旧文件时, 那些为了归档而创建的目录也会被删除。 -->
            <MaxHistory>50</MaxHistory>
            <!-- 当日志文件超过maxFileSize指定的大小时,根据上面提到的%i进行日志文件滚动 -->
            <maxFileSize>100MB</maxFileSize>
            <!-- 设置文件总大小 -->
            <totalSizeCap>20GB</totalSizeCap>
        </rollingPolicy>
        
        <!-- 日志输出格式-->
        <encoder charset="utf-8">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="FILE" />
    </root>
</configuration>

测试

运行测试方法,我们可以在指定目录看到生成的日志文件。

file_appender_01

查看日志文件,可以看到只打印了 error 等级的日志:

file_appender_02

将日志输出到数据库

logback 提供了DBAppender来支持将日志输出到数据库中。

创建表

logback 为我们提供了三张表用于记录日志, 在使用DBAppender之前,这三张表必须存在。

这三张表分别为:logging_event, logging_event_property 与 logging_event_exception。logback 自带 SQL 脚本来创建表,这些脚本在 logback-classic/src/main/java/ch/qos/logback/classic/db/script 文件夹下,相关脚本也可以再本项目的 resources/script 找到。

logback日志表脚本

由于本文使用的是 mysql 数据库,执行以下脚本(注意,官方给的 sql 中部分字段设置了NOT NULL 的约束,可能存在插入报错的情况,可以考虑调整):

BEGIN;
DROP TABLE IF EXISTS logging_event_property;
DROP TABLE IF EXISTS logging_event_exception;
DROP TABLE IF EXISTS logging_event;
COMMIT;

BEGIN;
CREATE TABLE logging_event 
  (
    timestmp         BIGINT NOT NULL,
    formatted_message  TEXT NOT NULL,
    logger_name       VARCHAR(254) NOT NULL,
    level_string      VARCHAR(254) NOT NULL,
    thread_name       VARCHAR(254),
    reference_flag    SMALLINT,
    arg0              VARCHAR(254),
    arg1              VARCHAR(254),
    arg2              VARCHAR(254),
    arg3              VARCHAR(254),
    caller_filename   VARCHAR(254),
    caller_class      VARCHAR(254) NOT NULL,
    caller_method     VARCHAR(254) NOT NULL,
    caller_line       CHAR(4) NOT NULL,
    event_id          BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
  );
COMMIT;

BEGIN;
CREATE TABLE logging_event_property
  (
    event_id          BIGINT NOT NULL,
    mapped_key        VARCHAR(254) NOT NULL,
    mapped_value      TEXT,
    PRIMARY KEY(event_id, mapped_key),
    FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
  );
COMMIT;

BEGIN;
CREATE TABLE logging_event_exception
  (
    event_id         BIGINT NOT NULL,
    i                SMALLINT NOT NULL,
    trace_line       VARCHAR(254) NOT NULL,
    PRIMARY KEY(event_id, i),
    FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
  );
COMMIT;

可以看到生成了三个表:

logback的三张日志表

配置文件

logback 支持使用 DataSourceConnectionSource,DriverManagerConnectionSource 与 JNDIConnectionSource 三种方式配置数据源 。本文选择第一种,并使用以 c3p0 作为数据源(第二种方式文中也会给出)。

这里需要说明下,因为实例化 c3p0 的数据源对象ComboPooledDataSource时,会去自动加载 classpath 下名为 c3p0-config.xml 的配置文件,所以,我们不需要再去指定 dataSource 节点下的参数,如果是 druid 或 dbcp 等则需要指定。

<configuration>

    <!--数据库输出-->
    <appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
        <!-- 使用jdbc方式 -->
        <!-- <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
            <driverClass>com.mysql.cj.jdbc.Driver</driverClass>
            <url>jdbc:mysql://localhost:3306/github_demo?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=GMT%2B8&amp;useSSL=true</url>
            <user>root</user>
            <password>root</password>
        </connectionSource> -->
        <!-- 使用数据源方式 -->
        <connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
           <dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource">
           </dataSource>
        </connectionSource>
    </appender>
        
    <root level="info">
        <appender-ref ref="DB" />
    </root>
</configuration>

测试

运行测试方法,可以看到数据库中插入了以下数据:

logback日志表数据

使用JMX配置logback

logback 支持使用 JMX 动态地更新配置。开启 JMX 非常简单,只需要增加 jmxConfigurator 节点就可以了,如下:

<configuration scan="true" scanPeriod="10 seconds" debug="true">

    <!-- 定义变量 -->
    <property scope="system" name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
    
    <!-- 开启JMX支持 -->
    <jmxConfigurator />
    
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    
       <target>system.err</target>
       
        <encoder charset="utf-8">
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
        
    </appender>
    
    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

在我们通过 jconsole 连接到服务器上之后(jconsole 在 JDK 安装目录的 bin 目录下),在 MBeans 面板上,在 "ch.qos.logback.classic.jmx.Configurator" 文件夹下你可以看到几个选项。如下图所示:

logback_jmx_01

我们可以看到,在属性中,我们可以查看 logback 已经产生的 logger 和 logback 的内部状态,通过操作,我们可以:

  • 获取指定 logger 的级别。返回值可以为 null
  • 设置指定的 logger 的级别。想要设置为 null,传递 "null" 字符串就可以
  • 通过指定的文件重新加载配置
  • 通过指定的 URL 重新加载配置
  • 使用默认配置文件重新加载 logback 的配置
  • 或者指定 logger 的有效级别

更多 JMX 相关内容可参考我的另一篇博客:如何使用JMX来管理程序?

补充--两种打印方式

实际项目中,有时我们需要对打印的内容进行一定处理,如下:

logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));

这种情况会产生构建消息参数的成本,为了避免以上损耗,可以修改如下:

if(logger.isDebugEnabled()) { 
  logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}

当我们打印的是一个对象时,也可以采用以下方法来优化:

// 不推荐
logger.debug("The new entry is " + entry + ".");
// 推荐
logger.debug("The new entry is {}", entry);

配置文件详解

前面已经说过, logback 配置文件名可以为 logback-test.xml 、 logback.groovy 或 logback.xml ,除了采用配置文件方式, logback 也支持使用 SPI 机制加载 ch.qos.logback.classic.spi.Configurator 的实现类来进行配置。以下讲解仅针对 xml 格式文件的配置方式展开。

另外,如果想要自定义配置文件的名字,可以通过系统属性指定:

-Dlogging.config=classpath:logback-sit.xml

如果没有加载到配置,logback 会调用 BasicConfigurator 进行默认的配置。

configuration

configuration 是 logback.xml 或 logback-test.xml 文件的根节点。

logback_configuration_01

configuration 主要用于配置某些全局的日志行为,常见的配置参数如下:

属性名 描述
debug 是否打印 logback 的内部状态,开启有利于排查 logback 的异常。默认 false
scan 是否在运行时扫描配置文件是否更新,如果更新时则重新解析并更新配置。如果更改后的配置文件有语法错误,则会回退到之前的配置文件。默认 false
scanPeriod 多久扫描一次配置文件是否修改,单位可以是毫秒、秒、分钟或者小时。默认情况下,一分钟扫描一次配置文件。

配置方式如下:

<configuration debug="true" scan="true" scanPeriod="60 seconds" >
    
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
       <target>system.err</target>   
        <encoder charset="utf-8">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

使用以上配置进行测试:

logback_configuration_debug.png

如上图,通过控制台我们可以查看 logback 加载配置的过程,这时,我们尝试修改 logback 配置文件的内容:

logback_configuration_scan.png

观察控制台,可以看到配置文件重新加载:

logback_configuration_scan2.png

logger

前面提到过,logger 是为我们打印日志的对象,这个概念非常重要,有助于更好地理解 logger 的继承关系。

在以下代码中,我们可以在getLogger方法中传入的是当前类的 Class 对象或全限定类名,本质上获取到的都是一个 logger 对象(如果该 logger 不存在,才会创建)。

    @Test
    public void test01() {
        Logger logger1 = LoggerFactory.getLogger(LogbackTest.class);
        Logger logger2 = LoggerFactory.getLogger("cn.zzs.logback.LogbackTest");
        System.err.println(logger == logger2);// true
    }

这里补充一个问题,该 logger 对象以 cn.zzs.logback.LogbackTest 命名,和我们配置文件中定义的 root logger 并不是同一个,但是为什么这个 logger 对象却拥有 root logger 的行为?

这要得益于 logger 的继承关系,如下图:

logback_logger_01

如果我们未指定当前 logger 的日志等级,logback 会将其日志等级设置为最近父级的日志等级。另外,默认情况下,当前 logger 也会继承最近父级持有的 appender。

下面测试下以上特性,将配置文件进行如下修改:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds" debug="true">


    <!-- 定义变量 -->
    <property scope="system" name="LOG_HOME" value="D:/growUp/test/logs" />
    <property scope="system" name="APP_NAME" value="logback-demo"/>
    <property scope="system" name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
    
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    
       <target>system.err</target>
       
        <encoder charset="utf-8">
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
    </appender>
    
    <timestamp key="bySecond" datePattern="yyyy-MM-dd'T'HH-mm-ss" />
    <!-- 文件输出 -->
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <append>true</append>
        <file>${LOG_HOME}/${APP_NAME}/file-${bySecond}.log</file>
        <immediateFlush>true</immediateFlush>
        <!-- 是否启用安全写入 -->
        <prudent>false</prudent>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
    </appender>
    
    <logger name="cn.zzs" level="error">
        <appender-ref ref="FILE" />
    </logger>
    
    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

这里自定义了一个 logger,日志等级是 error,appender 为文件输出。运行测试方法:

logback_logger_02

可以看到,名为 cn.zzs.logback.LogbackTest 的 logger 继承了名为 cn.zzs 的 logger 的日志等级和 appender,以及继承了 root logger 的 appender。

实际项目中,如果不希望继承父级的 appender,可以配置 additivity="false" ,如下:

    <logger name="cn.zzs" additivity="false">
       <appender-ref ref="FILE" />
    </logger>

注意,因为以下配置都是建立在 logger 的继承关系上,所以这部分内容必须很好地理解。

appender

appender 用于定义日志的输出目的地和输出格式,被 logger 所持有。logback 为我们提供了以下几种常用的appender:

类名 描述
ConsoleAppender 将日志通过 System.out 或者 System.err 来进行输出,即输出到控制台。
FileAppender 将日志输出到文件中。
RollingFileAppender 继承自 FileAppender,也是将日志输出到文件,但文件具有轮转功能。
DBAppender 将日志输出到数据库
SocketAppender 将日志以明文方式输出到远程机器
SSLSocketAppender 将日志以加密方式输出到远程机器
SMTPAppender 将日志输出到邮件

本文仅会讲解前四种,后四种可参考官方文档

ConsoleAppender

ConsoleAppender 支持将日志通过 System.out 或者 System.err 输出,即输出到控制台,常用属性如下:

属性名 类型 描述
encoder Encoder 后面单独讲
target String System.out 或 System.err。默认为 System.out
immediateFlush boolean 是否立即刷新。默认为 true。
withJansi boolean 是否激活 Jansi 在 windows 使用 ANSI 彩色代码,默认值为 false。 在windows电脑上我尝试开启这个属性并引入 jansi 包,但老是报错,暂时没有解决方案。

具体配置如下:

    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    
       <target>system.err</target>
       
        <encoder charset="utf-8">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

FileAppender

FileAppender 支持将日志输出到文件中,常用属性如下:

属性名 类型 描述
append boolean 是否追加写入。默认为 true
encoder Encoder 后面单独讲
immediateFlush boolean 是否立即刷新。默认为 true。
file String 要写入文件的路径。如果文件不存在,则新建。
prudent boolean 是否采用安全方式写入,即使在不同的 JVM 或者不同的主机上运行 FileAppender 实例。默认的值为 false。

具体配置如下:

    <!-- 定义变量 -->
    <property scope="system" name="LOG_HOME" value="D:/growUp/test/logs" />
    <property scope="system" name="APP_NAME" value="logback-demo"/>
    <property scope="system" name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
    <timestamp key="bySecond" datePattern="yyyy-MM-dd'T'HH-mm-ss" />
    
    <!-- 文件输出 -->
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>${LOG_HOME}/${APP_NAME}/file-${bySecond}.log</file>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
    </appender>

RollingFileAppender

RollingFileAppender 继承自 FileAppender,也是将日志输出到文件,但文件具有轮转功能。

RollingFileAppender 的属性如下所示:

属性名 类型 描述
file String 要写入文件的路径。如果文件不存在,则新建。
append boolean 是否追加写入。默认为 true。
immediateFlush boolean 是否立即刷新。默认为true。
encoder Encoder 后面单独将
rollingPolicy RollingPolicy 定义文件如何轮转。
triggeringPolicy TriggeringPolicy 定义什么时候发生轮转行为。如果 rollingPolicy 使用的类已经实现了 triggeringPolicy 接口,则不需要再配置 triggeringPolicy,例如 SizeAndTimeBasedRollingPolicy。
prudent boolean 是否采用安全方式写入,即使在不同的 JVM 或者不同的主机上运行 FileAppender 实例。默认的值为 false。

具体配置如下:

    <!-- 定义变量 -->
    <property scope="system" name="LOG_HOME" value="D:/growUp/test/logs" />
    <property scope="system" name="APP_NAME" value="logback-demo"/>
    <property scope="system" name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>    
    <!-- 轮转文件输出 -->
    <appender name="FILE-ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
        
        <!-- 轮转策略,它根据时间和文件大小来制定轮转策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 按天轮转  -->
            <fileNamePattern>${LOG_HOME}/${APP_NAME}/log-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <!-- 保存 30 天的历史记录,最大大小为 30GB -->
            <MaxHistory>30</MaxHistory>
            <totalSizeCap>30GB</totalSizeCap>
            <!-- 当日志文件超过100MB的大小时,根据上面提到的%i进行日志文件轮转 -->
            <maxFileSize>100MB</maxFileSize>
        </rollingPolicy>
        
        <!-- 日志输出格式-->
        <encoder charset="utf-8">
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
    </appender>

DBAppender

参见使用例子。

encoder

encoder 负责将日志事件按照配置的格式转换为字节数组,常用属性如下:

属性名 类型 描述
pattern String 日志打印格式。
outputPatternAsHeader boolean 是否将 pattern 字符串插入到日志文件顶部。默认false。

针对 pattern 属性,这里补充下它的常用转换字符:

转换字符 描述
c{length} lo{length} logger{length} 输出 logger 的名字。可以通过 length 缩短其长度。 但是,logger 名字最右边永远都会存在。 例如,当我们设置 logger{0}时,cn.zzs.logback.LogbackTest 中的 LogbackTest 永远不会被删除
C{length} class{length} 输出发出日志请求的类的全限定名称。 可以通过 length 缩短其长度。
d{pattern} date{pattern} d{pattern, timezone} date{pattern, timezone} 输出日志事件的日期。 可以通过 pattern 设置日期格式,timezone 设置时区。
m / msg / message 输出与日志事件相关联的,由应用程序提供的日志信息。
M / method 输出发出日志请求的方法名。
p / le / level 输出日志事件的级别。
t / thread 输出生成日志事件的线程名。
n 输出平台所依赖的行分割字符。
F / file 输出发出日志请求的 Java 源文件名。
caller{depth} caller{depthStart..depthEnd} caller{depth, evaluator-1, ... evaluator-n} caller{depthStart..depthEnd, evaluator-1, ... evaluator-n} 输出生成日志的调用者所在的位置信息。
L / line 输出发出日志请求所在的行号。
property{key} 输出属性 key 所对应的值。

注意,在拼接 pattren 时,应该考虑使用“有意义的”转换字符,避免产生不必要的性能开销。具体配置如下:

    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
       
        <encoder charset="utf-8">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <outputPatternAsHeader>true</outputPatternAsHeader>
        </encoder>
    </appender>

其中, 转换说明符 %-5level 表示日志事件的级别的字符应该向左对齐,保持五个字符的宽度。

filter

appender 除了定义日志的输出目的地和输出格式,其实也可以对日志事件进行过滤输出,例如,仅输出包含指定字符的日志。而这个功能需配置 filter。

LevelFilter

LevelFilter 基于级别来过滤日志事件。修改配置文件如下:

<configuration scan="true" scanPeriod="10 seconds" debug="true">

    <!-- 定义变量 -->
    <property scope="system" name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
    
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    
       <target>system.err</target>
       
        <encoder charset="utf-8">
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
       
       <!-- 设置过滤器 -->
       <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>