三、logback详解

363 阅读7分钟

一,Logback

1.1 概述

Logback 是由 Log4j 的创始人设计的一个现代化的日志框架,是 SLF4J 的原生实现。

  • 特点
    • 高性能:采用异步日志机制,可以将日志操作和业务逻辑分离,从而大幅度提升系统的性能。
    • 灵活的配置:支持多种不同的配置方式,包括基于XML、Groovy、JSON等格式的配置文件,同时还支持通过代码进行配置。
    • 多种日志级别:支持6种不同的日志级别,包括TRACE、DEBUG、INFO、WARN、ERROR和FATAL,用户可以根据需要选择合适的日志级别。
    • 多种输出方式:支持多种不同的输出方式,包括控制台输出、文件输出、邮件发送等。用户可以根据需要选择合适的输出方式。
    • 插件丰富:提供许多有用的插件,例如logstash-logback-encoder,可以将日志输出到ELK(Elasticsearch、LogstashKibana)平台上进行分析和可视化。
    • 易于集成:Logback与Spring、Hibernate、JUnit等框架都有很好的集成,可以方便地进行应用程序的日志输出管理。

image-20240608143930229

1.2 配置文件结构

Logback 的配置文件通常是 XML 格式,文件名为 logback.xml。它的结构如下:

  • configuration : 配置文件的根元素,它包含了多个子元素:appender、logger 和 root
    • appender : 定义了输出端,可以是控制台、文件、网络等,每一个appender都需要有一个唯一的名称和一个类定义
    • logger : 定义了日志记录器,用于记录指定类的日志信息。logger元素需要指定一个名称和一个级别,以及一个可选的appender-ref子元素,用于将日志记录器连接到一个appender
    • root : 定义了根日志记录器,它会接收所有未被其他logger接收的日志事件
<!--
scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <!--
     定义变量值的标签,property标签有两个属性,name和value;过property定义的值会被插入到logger上下文中。定义变量后,可以使${name}来使用变量
     -->
    <property name="AppName" value="demo"/>
    <!--
    每个logger都关联到logger上下文,默认上下文名称为“default”。但可以使用contextName标签设置成其他名字,用于区分不同应用程序的记录
    -->
    <contextName>${AppName}</contextName>
    
    <!--负责写日志的组件-->
    <appender>
    </appender>
    <!--用来设置某一个包或者具体的某一个类的日志打印级别以及指定appender。-->
    <logger>
        <appender-ref ref=""/>
    </logger>
    <!---根logger,也是一种logger,且只有一个level属性-->
    <root>
    </root>
    
</configuration>

1.3 日志级别

  • ALL:最低等级的,用于打开所有日志记录
  • TRACE:是最详细的日志信息,通常用于诊断问题和追踪代码执行流程**。在生产环境中,应该关闭TRACE级别的日志输出,以避免影响系统性能**
  • DEBUG:是调试信息,用于调试应用程序。在生产环境中,建议将DEBUG级别的日志输出关闭
  • INFO:是普通的信息记录,通常用于向用户展示可读的操作结果或执行状态。例如,当用户成功登录时,可以记录一条 INFO 级别的日志
  • WARN:表示警告信息,通常用于记录一些不严重但需要注意的问题。例如,当系统资源紧张时,可以记录一条WARN级别的日志
  • ERROR:表示错误信息,通常用于记录系统运行时发生的错误。例如,当数据库连接失败时,可以记录一条ERROR级别的日志
  • FATAL:表示致命错误,通常用于记录系统崩溃或无法恢复的错误。例如,当出现内存泄漏或硬盘损坏时,可以记录一条FATAL级别的日志
  • OFF:最高等级的,用于关闭所有日志记录。

二,Logback快速入门

  • logback坐标

    不写版本的原因是SpringBoot自带集成logback,只需引入即可

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
    </dependency>
    

    如果引入了jdbc就不需要引入logback,jdbc里面引入了loback的

  • 配置文件编写

    springboot集成了logback日志系统**,默认读取名叫logback-spring.xml的配置文件**,如果想自定义配置文件的名称,需要在spring boot配置文件中配置指定

    logging:
    	config: classpath:logback.xml
    
  • 用法:

    public class MyTest {
        private static final Logger logger = LoggerFactory.getLogger(MyTest.class);
    
        public static void main(String[] args) {
            logger.info("info.....");
            logger.warn("warn" + ".....");
            logger.error("error,msg={}", "error....");
        }
    }
    
  • 与lombok集成

    logback和lombok集成,使用@Slf4j注解标注类,直接使用log对象,需添加lombok依赖

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    
    @Slf4j
    public class MyTest {
        public static void main(String[] args) {
            log.info("info.....");
            log.warn("warn" + ".....");
            log.error("error,msg={}", "error....");
        }
    }
    

三,logback配置文件详解

logback组件之间的关系:

  1. Logger:日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。

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

  3. Layout:负责把事件转换成字符串,格式化的日志信息的输出。在logback中Layout对象被封装在encoder中。

3.1 记录器logger

3.1.1 记录器的层级结构:

**记录器有一个默认的root(根记录器)**每当我们创建一个logger就会如下图的结构

image-20240609161902448

3.1.2 记录器的属性

记录器有三个属性:

  1. name属性:记录器的名字(一般设置为当前类的全类名
  2. level属性(可选):
    • 记录器的级别,从低到高:TRACE<DEBUG<INFO<WARN<ERROR<FATAL
    • 假设当前记录器级别为INFO那么只能打印级别打印等于INFO的记录
    • 如果记录器未设置level属性,则该记录器的级别从上级记录器继承
    • root(根记录器只有level属性)
  3. additivity属性(可选):
    • 是否允许叠加打印日志true/false,就说如果你通过上图的org.example.APP打印日志,那么他上分所有的日志记录器都会执行,这就是叠加打印。默认false

配置文件举例:

<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <logger name="com.aaa" level="info">
        
    </logger>
</configuration>

3.2 附加器Appender

记录器会将输出日志的任务交给附加器完成,不同的附加器会将日志输出到不同的地方,比如控制台附加器、文件附加器、网络附加器等等。

常用的附加器:

  • 控制台附加器:ch.qos.logback.core.ConsoleAppender
  • 文件附加器:ch.gos logback.core.FileAppender
  • 滚动文件附加器:ch.gos.logback.core.rolling.RollingFileAppender

3.2.1控制台附加器

<configuration scan="true" scanPeriod="60 seconds" debug="false">
    
     <!--定义了输出日志的格式到log的格式-->
    <property name="pattern" 
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{56}.%method:%L - %msg%n"/>
    
     <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!--控制输出流对象 默认 System.out(黑色) 改为 System.err(红色)-->
        <target>System.out</target>
        <!--encoder中对日志进行格式化-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出-->
            <pattern>${pattern}</pattern>
            <charset>utf-8</charset>
        </encoder>
    </appender>
    
    
	<logger name="org.hibernate.SQL">
    	<appender-ref ref="STDOUT"/>
    </logger>
    
</configuration>

3.2.2 Pattern标签

  • 在Pattern标签中使用**%作为转义字符**,如果要表达出%就使用%

  • %c{length}:输出日志的logger名,可有一个整形参数,功能是缩短logger名,设置为0表示只输入 logger最右边点符号之后的字符串。

    • 如果logger名的长度>length:就输出简写内容
    • 如果logger名的长度<length:就输出全部内容
    • 相同的还有**%lo{length},%logger{length},%C{length},%class{length}**
  • %contextName:输出上下文名称,默认是default

    需要在xml中添加标签

    <contextName>这个是我的上下文名称</contextName>
    
  • %d{pattern}:输出当前时间,语法是yyyy-MM-hh HH:mm:ss.SS:年月日,时分秒毫秒

    也可以写成**%date{pattern}**

  • %F或%File:输出当前执行这个log的java文件名(尽量避免使用)

  • %caller{depth}:输出生成日志的调用者的位置信息,整数选项表示输出信息的深度

  • %L或%line:输出执行日志请求的行号**(尽量避免使用)**

  • %m,%msg,%message:输出应用程序提供的信息(log.info("输出这里面的内容"))

  • %M,%method:输出执行日志请求的方法,(尽量避免使用)

  • %n:输出一个换行

  • **%p,%le,%level:**输出日志的级别

  • %r,%relative:输出从程序启动到创建日志记录的时间,单位是毫秒

  • **%t,%thread:**输出产生日志的线程名

  • %replace(p){r,t}:p为日志内容,r是正则表达式,将p中符合r的内容替换为t

    举例:%replace(%msg){'\s' , test}


截取格式化:

  • %20logger:

    举例:

    • 假设logger内容长度为10,那么就会左边补齐10个空格
    • 假设logger内容长度为23,那么就正常输出
  • %-20logger :第一个是补左侧,这个就是补右侧

  • %.20logger:

    • 假设logger长度大于20,那么就只截取,从最右侧开始累计20个字符的内容
    • 假设logger长度小于20,正常输出
  • %20.30logger

    • 如果logger长度小于20,先在左侧填满20,然后从最右侧开始截取
  • %-20.30logger

    • 如果logger长度小于20,先在右侧填满20,然后从最右侧开始截取
  • %-.20logger

    • 左侧第一个开始计算,如果logger长度大于20,那么只截取到第20个字符

3.2.3 文件附加器

文件附加器有以下子标签

  • file:文件名,可以是相对路径/绝对路径,如果上级路径不存在,就会自动创建,没有默认值。

  • append:文件是否追加,true是(默认),flase否

  • encoder:对记录事件进行格式化

  • prudent:根据不同类型有不同的效果

    • FileAppender:如果是true,日志会被安全的写入文件,即使其他的FileAppender也在向此文件做写入操作,效率低,默认是false。
    • RollingFileAppender:当为true时,不支持FixedwindowRollingPolicy。支持TimeBasedRollingPolicy, 但是有两个限制
      • 不支持也不允许文件压缩

      • 不能设置fil属性,必须留空。

    layout和encoder的区别:白从0.9.19版本之后,Fileappender和他的子类是期望使用encoder,不再推荐使用layout。

<configuration scan="true" scanPeriod="60 seconds" debug="false">
    
     <!--定义了输出日志的格式到log的格式-->
    <property name="pattern" 
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{56}.%method:%L - %msg%n"/>
    
     <!-- 输出到文件 -->
    <appender name="File" class="ch.gos logback.core.FileAppender">
        <!--日志消息格式配置-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>${pattern}</pattern>
            <charset>utf-8</charset>
        </encoder>
        <!--输出到指定的mylog.log文件中-->
        <file>mylog.log</file>
        <!--默认true,代表每次将日志内容插入到mylog.log中,如果是false代表刷新-->
        <append>true</append>
    </appender>
    
    
	<logger name="org.hibernate.SQL">
    	<appender-ref ref="File"/>
    </logger>
    
</configuration>

3.2.4 滚动文件附加器

<configuration scan="true" scanPeriod="60 seconds" debug="false">
    
     <!--定义了输出日志的格式到log的格式-->
    <property name="pattern" 
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{56}.%method:%L - %msg%n"/>
    
     <!-- 输出到文件 -->
    <appender name="RollFile" class="ch.gos.logback.core.rolling.RollingFileAppender">
        <!--输出到指定的mylog.log文件中-->
        <file>mylog.log</file>
        <!--日志消息格式配置-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>${pattern}</pattern>
            <charset>utf-8</charset>
        </encoder>
        
        <!--滚动策略 基于时间的-->
        <rollingPolicy class="ch.gos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--设置归档文件的生成模式
				yyyy-MM-dd就代表按天生成文件
				yyyy-MM-dd HH就代表按小时
				yyyy/yyyy-MM-dd HH就代表按小时生成文件,并将文件放到yyyy年这个文件夹中
			-->
            <fileNamePattern>%d{yyyy-MM-dd}.log</fileNamePattern>
             <!--设置归档文件最大生成数量,超过的就删除旧的-->
            <maxHistory>3</maxHistory>
            <!--设置所有文件的总大小,超过就会删除超过的部分-->
            <totalSizeCap>5GB</totalSizeCap>
        </rollingPolicy>
        
         <!--滚动策略 基于时间和大小的-->
        <rollingPolicy class="ch.gos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!--设置归档文件的生成模式
				yyyy-MM-dd就代表按天生成文件
				yyyy-MM-dd HH就代表按小时
				yyyy/yyyy-MM-dd HH就代表按小时生成文件,并将文件放到yyyy年这个文件夹中
			-->
            <fileNamePattern>%d{yyyy-MM-dd}.%i.log</fileNamePattern>
             <!--设置归档文件最大生成数量,超过的就删除旧的-->
            <maxHistory>3</maxHistory>
            <!--设置所有文件的总大小,超过就会删除超过的部分-->
            <totalSizeCap>5GB</totalSizeCap>
            <!--文件超过50MB就归档为一个文件,这个是这个策略独有的-->
            <maxFileSize>50Mb</maxFileSize>
        </rollingPolicy>
        
    </appender>

	<logger name="org.hibernate.SQL">
    	<appender-ref ref="RollFile"/>
    </logger>
    
</configuration>

目前有两种滚动策略:

  1. 基于时间的滚动策略

    TimeBasedRollingPolicy

  2. 基于时间和大小的滚动策略

    SizeAndTimeBasedRollingPolicy

3.3 过滤器

过滤器有很多,但只看:

  • LevelFilter:

     <!-- 只记录INFO级别的日志 -->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>FATAL</level>
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
    
  • ThresholdFilter:

    <!-- 只有DEBUG及以上级别的才输出 -->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>DEBUG</level>
    </filter>
    

在logback中,logger,layout,appender都可自己自定义写,写出自己需求的过滤器等,具体写法去看官网即可

3.4 示例配置

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

    <property name="LOG_CONTEXT_NAME" value="web-app"/>
    <!--定义日志文件的存储地址 勿在LogBack的配置中使用相对路径-->
    <property name="LOG_HOME" value="logs/${LOG_CONTEXT_NAME}"/>
    <!-- 定义日志上下文的名称 -->
    <contextName>${LOG_CONTEXT_NAME}</contextName>
    <!--定义了输出日志的格式到log的格式-->
    <property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{56}.%method:%L - %msg%n"/>
    <!--定义了输出日志的格式到html的格式-->
    <property name="pattern-html" value="%-5level%d{yyyy-MM-dd HH:mm:ss.SSS}%c%M%L%thread%m"/>

    <!-- 控制台输出 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!--控制输出流对象 默认 System.out(黑色) 改为 System.err(红色)-->
        <target>System.out</target>
        <!--日志消息格式配置-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>${pattern}</pattern>
            <charset>utf-8</charset>
        </encoder>
        <!--日志级别过滤器LevelFilter-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>DEBUG</level>
        </filter>
    </appender>

    <!--info日志统一输出-->
    <appender name="file.info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--启用审慎模式:当启用审慎模式时,Logback会确保在多进程或多实例环境中,多个进程或实例可以安全地共享同一个日志文件。-->
        <Prudent>true</Prudent>
        <!--日志拆分规则-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名,按小时生成输出log文件-->
            <!--<FileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}/info/info.%d{yyyy-MM-dd-HH}.%i.log</FileNamePattern>-->
            <!--日志文件输出的文件名,按小时生成输出html文件-->
            <FileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}/info/info.%d{yyyy-MM-dd-HH}.%i.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!--日志文件不能超过10MB-->
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>

        <!--日志消息格式配置-输出log文件-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出-->
            <pattern>${pattern}</pattern>
            <charset>utf-8</charset>
        </encoder>

        <!-- 只记录INFO级别的日志 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--  日志输出级别 -->
    <root level="ALL">
        <appender-ref ref="console"/>
        <appender-ref ref="file.info"/>
    </root>

    <!-- 某类/包下的所有日志使用file.info appender输出-->
    <!--<logger name="cn.ybzy.demo.controller.TestController" additivity="false">-->
    <!--    <appender-ref ref="file.info"/>-->
    <!--</logger>-->

    <!--<logger name="org.hibernate.SQL">-->
    <!--    <level value="DEBUG"/>-->
    <!--</logger>-->

</configuration>