# Logback

# Logback简介

Logback 是由 log4j 创始人设计的又一个开源日志组件。

Logback 当前分成三个模块: logback-core,logback-classic和logback-access。

logback-core 是其它两个模块的基础模块。

logback-classic 是 log4j的一个改良版本。此外 logback-classic 完整实现SLF4J API。使你可以很方便地更换成其它日志系统如 log4j或JDK14 Logging。

logback-access 访问模块与 Servlet 容器集成提供通过 Http 来访问日志的功能。

# Logback中的组件

  • Logger:日志的记录器,主要用于存放日志对象,也可以定义日志类型、级别。

  • Appender:用于指定日志输出的目的地,目的地可以是控制台、文件、数据库等等。

  • Layout:负责把事件转换成字符串,格式化的日志信息的输出。在Logback 中Layout 对象被封装在encoder中.也就是说我们未来使用的 encoder 其实就是 Layout

# Logback配置文件

Logback提供了3种配置文件。

  • logback.groovy
  • logback-test.xml
  • logback.xml

如果都不存在则采用默认的配置。

# 日志输出格式

标识符及占位符 描述
%-10level 级别,设置10个这符,左对齐
%d{yyyy-MM-dd HH:mm:ss.SSS} 日期时间
%c 当前类全限定名
%M 当前执行日志的方法
%L 行号
%thread 线程名称
%m或者%msg 输出的日志信息
%n 换行

# 案例

# 引入依赖

我们已经学习了如何使用slf4j,那么我们就使用slf4j+logback的方式

		<dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>

        <!--slf4j日志门面 核心依赖-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.32</version>
        </dependency>

        <!--
           logback-core是logback-classic的基础模块
           logback-classic已经涵盖了 logback-core,
           Maven有依赖传递性,会自动依赖logback-core
        -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.10</version>
        </dependency> 			
代码已经复制到剪贴板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 日志级别

logback有5种级别的日志输出,分别是 trace < debug < info < warn < error 默认级别:debug

	@Test
    public void testLogback(){

        Logger logger = LoggerFactory.getLogger(LogbackTest.class);
        logger.error("========= ERROR信息 test Logback =========");
        logger.warn("========= WARN信息 test Logback =========");
        logger.info("========= INFO信息 test Logback =========");
        logger.debug("========= DEBUG信息 test Logback =========");
        logger.trace("========= TRACE信息 test Logback =========");

        /**
         *通过控制台信息,可以看到默认级别是debug。 trace级别的信息没有输出
         
         * 17:18:06.755 [main] ERROR cn.giteasy.logback.test.LogbackTest - ========= ERROR信息 test Logback =========
         * 17:18:06.757 [main] WARN cn.giteasy.logback.test.LogbackTest - ========= WARN信息 test Logback =========
         * 17:18:06.757 [main] INFO cn.giteasy.logback.test.LogbackTest - ========= INFO信息 test Logback =========
         * 17:18:06.757 [main] DEBUG cn.giteasy.logback.test.LogbackTest - ========= DEBUG信息 test Logback =========
         */

    }
代码已经复制到剪贴板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 使用配置文件

logback.xml

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>

    <!--
        <property name="" value=""></property>
        配置文件通用属性,通过${name}的形式取值
    -->
    
    <property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %c %M %L %m%n"></property>


    <!-- 控制台Appender -->
    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <!--
            输出目标的配置,
            System.out:以黑色字体(默认)
            System.err:红色字体
        -->
        <target>
            System.err
        </target>
        <!-- 日志输出格式 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>


    
    <!--
        日志记录器配置,可以配置多个Appender,进行多方向的日志输出
        root => rootLogger
        level:  表示日志级别
    -->
    <root level="ALL">
        <appender-ref ref="consoleAppender"/>
    </root>
 
</configuration>


@Test
public void testLogbackConfigFile(){

    Logger logger = LoggerFactory.getLogger(LogbackTest.class);
    logger.error("========= ERROR信息 test Logback =========");
    logger.warn("========= WARN信息 test Logback =========");
    logger.info("========= INFO信息 test Logback =========");
    logger.debug("========= DEBUG信息 test Logback =========");
    logger.trace("========= TRACE信息 test Logback =========");
}
代码已经复制到剪贴板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# 输出日志到文件中

在实际生产环境中,我们更希望将日志信息保存到文件中

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>

   
    <property name="logDir" value="D://logback_log"></property>
    <property name="fileName" value="logback.log"></property>
    <property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %c %M %L %m%n"></property>


    <!--文件appender,默认是以追加日志的形式进行输出的-->
    <appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
        <!--输出文件位置-->
        <file>${logDir}//${fileName}</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    
    <root level="ALL">
        <appender-ref ref="fileAppender"/>
    </root>

</configuration>
代码已经复制到剪贴板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 输出日志为HTML文件格式

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>

   
    <property name="logDir" value="D://logback_log"></property>
    <property name="htmlFileName" value="logback.html"></property>
		<property name="htmlPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS}%thread%-5level%c%M%L%m"></property>

    <!--HTML文件appender-->
    <appender name="htmlFileAppender" class="ch.qos.logback.core.FileAppender">
        <!--输出文件位置-->
        <file>${logDir}//${htmlFileName}</file>
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="ch.qos.logback.classic.html.HTMLLayout">
                <pattern>${htmlPattern}</pattern>
            </layout>
        </encoder>
    </appender>

    
    <root level="ALL">
        <!-- <appender-ref ref="fileAppender"/> -->
        <appender-ref ref="htmlFileAppender"/>
    </root>

</configuration>
代码已经复制到剪贴板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 日志文件拆分和归档压缩

重要标签来源: 查看源码 RollingFileAppender类中找到rollingPolicy属性 SizeAndTimeBasedRollingPolicy类中找到maxFilesize属性 这些属性在类中都是以set方法的形式进行的赋值 我们在配置文件中配置的信息,其实找到的都是这些属性的set方法 在TimeBasedRollingPolicy找到

   static final string FNP_NOT_SET =
         "The FileNamePattern option must be set before using TimeBasedRollingPolicy."
代码已经复制到剪贴板
1
2

只要我们要使用到日志的拆分 FileNamePattern属性是必须要使用到了

logback.xml

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>

   	<property name="logDir" value="D://logback_log"></property>
    <property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %c %M %L %m%n"></property>
    
  
    <!--可拆分归档的appender-->
    <appender name="rollFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
        <file>${logDir}/roll_logback.log</file>
        <!--指定拆分规则-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!--按照时间和压缩格式声明文件名,压缩格式gz-->
            <fileNamePattern>${logDir}/roll_logback.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
            <!--按照文件大小进行拆分-->
            <maxFileSize>2KB</maxFileSize>
        </rollingPolicy>
    </appender>

    
    <root level="ALL">
        <!-- <appender-ref ref="fileAppender"/> -->
        <!-- <appender-ref ref="htmlFileAppender"/>  -->
      	<appender-ref ref="rollFileAppender"/>
    </root>

</configuration>
代码已经复制到剪贴板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

为了看到拆分的效果,我们使用循环测试日志输出

	@Test
    public void testFileSplit(){
        Logger logger = LoggerFactory.getLogger(LogbackTest.class);
        for (int i = 0; i < 100; i++) {

            logger.error("========= ERROR信息 test Logback =========");
            logger.warn("========= WARN信息 test Logback =========");
            logger.info("========= INFO信息 test Logback =========");
            logger.debug("========= DEBUG信息 test Logback =========");
            logger.trace("========= TRACE信息 test Logback =========");
        }

    }
代码已经复制到剪贴板
1
2
3
4
5
6
7
8
9
10
11
12
13

# 使用过滤器

使用过滤器,对日志进行更细粒度的控制

logback.xml

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>

   	<property name="logDir" value="D://logback_log"></property>
    <property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %c %M %L %m%n"></property>
    
   <!--使用过滤器,进行细粒度控制-->
    <appender name="consoleFilterAppender" class="ch.qos.logback.core.ConsoleAppender">
        <target>
            System.err
        </target>
        <!-- 日志输出格式 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>

        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <!--高于level中设置的级别,则打印日志-->
            <onMatch>ACCEPT</onMatch>
            <!--低于level中设置的级别,则屏蔽-->
            <onMismatch>DENY</onMismatch>
        </filter>

    </appender>
   
    
    <root level="ALL">
      	<appender-ref ref="consoleFilterAppender"/>
    </root>

</configuration>
 	@Test
    public void test06(){
        Logger logger = LoggerFactory.getLogger(LogbackTest.class);
        logger.error("========= ERROR信息 test Logback =========");
        logger.warn("========= WARN信息 test Logback =========");
        logger.info("========= INFO信息 test Logback =========");
        logger.debug("========= DEBUG信息 test Logback =========");
        logger.trace("========= TRACE信息 test Logback =========");


        /**
         * 2022-01-28 21:27:30.784 [main] ERROR cn.giteasy.logback.test.LogbackTest test06 128 ========= ERROR信息 test Logback =========
         */

    }
代码已经复制到剪贴板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# 异步日志

 	@Test
    public void test07(){
        Logger logger = LoggerFactory.getLogger(LogbackTest.class);

        //日志输出
        for (int i = 0; i < 100; i++) {

            logger.error("========= ERROR信息 test Logback =========");
            logger.warn("========= WARN信息 test Logback =========");
            logger.info("========= INFO信息 test Logback =========");
            logger.debug("========= DEBUG信息 test Logback =========");
            logger.trace("========= TRACE信息 test Logback =========");
        }

        //业务逻辑操作
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }

    }
代码已经复制到剪贴板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

按照我们当前的代码执行顺序, 代码肯定是按照从上向下的顺序执行,上面的代码完全执行完毕后,才会执行下面的业务逻辑 由此得出会出现的问题: 只要是在记录日志,那么系统本身的功能就处于一种停滞的状态,当日志记录完毕后,才会执行其他代码 如果日志记录量非常庞大的话,那么系统本身业务代码的执行效率会非常低 所以logback为我们提供了异步日志的功能

# 配置方式

  1. 配置异步日志

在异步日志中引入我们真正需要输出的appender

    <appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="consoleAppender"/>
    </appender>
代码已经复制到剪贴板
1
2
3
  1. 在rootLogger中引入导步日志
	<root level="ALL">
       <appender-ref ref="asyncAppender"/>
 </root>
代码已经复制到剪贴板
1
2
3

完整配置文件logback.xml

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>


    <property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %c %M %L %m%n"></property>


    <!-- 控制台Appender -->
    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <target>
            System.err
        </target>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--异步日志Appender-->
    <appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="consoleAppender"/>
    </appender>
    

    <root level="ALL">
        <appender-ref ref="asyncAppender"/>
    </root>
 
</configuration>
代码已经复制到剪贴板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 输出效果

2022-02-12 19:10:25.437 [main] WARN  cn.giteasy.logback.test.LogbackTest test07 191 ========= WARN信息 test Logback =========
0
2022-02-12 19:10:25.437 [main] ERROR cn.giteasy.logback.test.LogbackTest test07 190 ========= ERROR信息 test Logback =========
1
2022-02-12 19:10:25.437 [main] WARN  cn.giteasy.logback.test.LogbackTest test07 191 ========= WARN信息 test Logback =========
2
代码已经复制到剪贴板
1
2
3
4
5
6

所谓异步日志的原理是: 系统会为日志操作单独的分配出来一个线程,主线程会继续向下执行 线程1:系统业务代码执行 线程2:打印日志 两个线程争夺CPU的使用权 在实际项目开发中,越大的项目对于日志的记录就越庞大,为了保证系统的执行效率,异步日志是不错的选择。

# 自定义Logger

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>

   
    <property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %c %M %L %m%n"></property>


    <!-- 控制台Appender -->
    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <target>
            System.err
        </target>
        <!-- 日志输出格式 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

 
    <!--
        自定义Logger
        additivity="false" 表示不继承rootLogger
      -->
    <logger name="cn.giteasy" level="warn" additivity="false">
        <appender-ref ref="consoleAppender"></appender-ref>
    </logger>

</configuration>
代码已经复制到剪贴板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 关于Logback的补充

  1. 异步日志 可配置属性:
 <discardingThreshold>0</discardingThreshold>
代码已经复制到剪贴板
1

​ 当队列的剩余容量小于这个阈值的时候,当前日志的级别 trace、debug、info这3个级别的日志将被丢弃 ​ 设置为0,说明永远都不会丢弃trace、debug、info这3个级

  <queueSize>256</queueSize>
代码已经复制到剪贴板
1

​ 配置队列的深度,这个值会影响记录日志的性能,默认值256 ​
​ 关于这两个属性,一般情况下,我们使用默认值即可,不要乱配置,会影响系统性能,了解其功能即可

  1. 配置文件转换 关于不同的日志实现,配置文件也是不同的: log4j一般使用的是properties属性文件 logback使用的是xml配置文件 如果我们遇到了一种需求,需要将正在使用的log4j,替换为logback,应该如何处理? 使用转换工具: 访问logback官网 找到log4j.properties转换器 (https://logback.qos.ch/translator/) 只有是二者兼容的配置,才会被翻译 如果是log4j独立的技术,logback没有,或者是有这个技术但是并不兼容转义 那么这个工具则不会为我们进行转换,如果是遇到简单的配置,我们可以使用工具。如果是配置比较繁多,复杂,建议手动进行配置。
上次更新: 2024/7/11