快速入门 Logback

简介

Logback 旨在作为流行的 log4j 项目的继承者。它是由 log4j 创始人 Ceki Gulcu 设计的。同时它也是 SpingBoot 项目的默认日志框架。

安装

因为 logback 需要和 slf4j 一起使用,所以总共需要添加依赖的包有 slf4j-api.jar,logback-core.jar,logback-classic.jar,logback-access.jar 这个暂时用不到所以不添加依赖了。

xml 复制代码
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <logback.version>1.1.7</logback.version>
    <slf4j.version>1.7.21</slf4j.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>${logback.version}</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>${logback.version}</version>
    </dependency>
</dependencies>

在 SpringBoot 项目中,如果你引入了 spring-boot-starter-web,那你已经引入了上述的所有依赖。

配置

Logback 的配置文件名为 logback.xml,在应用程序启动之后,Logback 会尝试在类路径中寻找该文件,如果没有找到,则会使用 Logback 内部的默认配置。

如果你的 logback.xml 配置文件在应用程序外部,可以通过环境变量 logback.configurationFile=/path/to/config.xml 来指定外部配置文件的路径。

configration

configuration 是 logback.xml 中的顶级元素,它包含了一些属性:

  • scan:布尔类型,如果为 true,则 Logback 会在配置文件修改之后自动重新加载配置。
  • scanPeriod:如果 scan 设置为 true,则可以使用该属性来配置 Logback 扫描配置文件是否变化的扫描周期。
  • debug:布尔类型,该属性表示是否打印 Logback 内部日志,如果为 true,则打印,默认为 false。
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds" debug="false">
  ...
</configuration> 

上述实例中表示开启配置文件扫描,扫描周期为 30s,并且不打印 Logback 内部日志。

configuration 元素下有三个子元素 appender,logger,root,其中 appender 和 logger 可以是零个或多个,root 元素则做多只能有一个。

property

用于配置变量,配置的变量可以在 appender 和 logger 中通过 ${} 来使用,可以用于统一配置。

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    
    <property name="PATTERN" value="%-4relative [%thread] %-5level %logger{35} - %msg %n" /> 
    
   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
      <encoder> 
         <pattern>${PATTERN}</pattern> 
      </encoder> 
   </appender> 

   <root level="DEBUG"> 
      <appender-ref ref="STDOUT" /> 
   </root> 
</configuration>

appender

appender 元素用来定义写日志的组件,它有两个必填的属性 name 和 class,name 表示组件的名称和 class 用于配置写日志逻辑的类,不同的类对应不同的写日志方式,目前,Logback 中定义了有 ConsoleAppender(控制台)、FileAppender(文件)、ServerSockerAppender(远程 Socker 服务器)、SMTPAppender(远程邮件服务器)、和 SyslogAppender(远程 Syslog 守护程序) 等 appender;当然,你也可以通过实现 Appender 接口来实现自己的 appender 并在配置文件中使用。

而这其中最常使用的就是 ConsoleAppender、FileAppender 和 RollingFileAppender。其中 RollingFileAppender 是 FileAppender 子类,它在日志写文件的基础上附加了滚动日志的功能,它解决了日志一直记录在一个文件而造成超大日志文件的痛点。

ConsoleAppender

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration> 
   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
      <encoder> 
         <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern> 
      </encoder> 
   </appender> 

   <root level="DEBUG"> 
      <appender-ref ref="STDOUT" /> 
   </root> 
</configuration>

上述示例中的名为 STDOUT 的 appender 使用了 ConsoleAppender 来输出日志,即将日志输出到控制台上,其中的 <encoder> 子标签定对日志进行格式化。

FileAppender

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration> 
  <appender name="FILE" class="ch.qos.logback.core.FileAppender"> 
    <file>testFile.log</file> 
    <append>true</append> 
    <encoder> 
      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> 
    </encoder> 
  </appender> 

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

上述示例中的名为 FILE 的 appender 使用了 FileAppender 来输出日志,即将日志输出到一个文件中,其中子标签释义如下:

  • file:记录日志的文件名,可以是相对目录也可以是绝对目录,如果上级目录不存在则自动创建
  • append:记录日志之前如果文件已经存在的行为,true 表示在现有文件的末尾追加日志,false 表示清空现有文件内容后写入日志,默认为 true
  • encoder:输出日志的格式

RollingFileAppender

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration> 
    <appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> 
            <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern> 
            <maxHistory>30</maxHistory> 
        </rollingPolicy> 
        <encoder> 
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> 
        </encoder> 
    </appender> 

    <root level="DEBUG"> 
        <appender-ref ref="ROLLING_FILE" /> 
    </root> 
</configuration>

上述示例中名为 ROLLING_FILE 的 appender 使用 RollingFileAppender 来输出日志,使用的滚动策略是基于时间的滚动策略,其中子标签释义如下:

  • rollingPolicy:滚动策略,常见的滚动策略有基于时间的滚动策略 TimeBasedRollingPolicy、基于文件大小的滚动策略 SizeBasedTriggeringPolicy
  • encoder:输出日志的格式
基于时间的滚动策略

类是 ch.qos.logback.core.rolling.TimeBasedRollingPolicy,常用属性如下:

  • fileNamePattern:必填属性,用于设定新文件的文件名和每次滚动的时间周期,通过 %d 来只当滚动周期,如 logFile.%d{yyyy-MM-dd}.log 表示按天滚动,logFile.%d{yyyy-MM}.log 表示按月滚动,logFile.%d.log 表示按天滚动
  • maxHistory:可选属性,与 fileNamePattern 搭配使用,用于设置可以保留的日志文件的个数。例如如果日志文件 1 个月滚动一次,maxHistory 设置为 5 表示最大保留最近 5 个月的日志文件,如果日志文件 1 天滚动一次,maxHistory 设置为 30 表示最大保留最近 30 天的日志文件
  • totalSizeCap:可选属性,相当于按时间滚动日志的保险,用来控制所有日志文件的总大小,当总大小超过指定的值时,会删除最老的日志文件
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logFile.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--             按天轮转 -->
            <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!--             保存 30 天的历史记录,最大大小为 3GB -->
            <maxHistory>30</maxHistory>
            <totalSizeCap>3GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="FILE" />
    </root>
</configuration>
基于时间和文件大小的滚动策略

类是 ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy,常用属性如下:

  • fileNamePattern:用法和基于时间的滚动策略一样,除了 %d,还需要使用 %i,用于解决没有到下一个滚动时间但是因为文件大小而触发的滚动,文件名一样的问题,%i 从 0 开始递增

  • maxFileSize:每个文件的最大大小,如果超过指定的值会触发滚动

  • maxHistory:用法和基于时间的滚动策略一样

  • totalSizeCap:用法和基于时间的滚动策略一样

logger

appender 标签用来配置日志的输出目的地,而 logger 标签可以看做日志的来源,即使从哪个包下或具体哪个类打印的日志,logger 标签可以绑定零个或多个 appender 来指定日志输出目的地。它有以下参数:

  • name:必填属性,指定一个包或具体的类
  • level:可选属性,日志的打印级别,如果没有指定则从父 logger 中继承
  • additivity:可选属性,是否向父 logger 传递日志,布尔类型,默认为 true,表示向父 logger 传递日志

logger 包含零个或多个子标签 <appender-ref> ,它用来绑定 appender。

在 logback 中,logger 具有树形结构的层级关系,这种层级关系通过包名来构建,如下所示:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration> 
   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
      <encoder> 
         <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern> 
      </encoder> 
   </appender>
    
    <logger name="com.test.logback" level="debug" additivity="true">
        <appender-ref ref="STDOUT" />
    </logger>
    
    <logger name="com.test" level="debug" additivity="true">
        <appender-ref ref="STDOUT" />
    </logger>

   <root level="debug"> 
   </root> 
</configuration>

上述两个 logger,通过包名的层级结构,name 为 com.test 的 logger 是 name 为 com.test.logback 的父 logger,如果通过包名的层级结构找不到该 logger 的父 logger,那么它的父 logger 就是 root logger,比如,name 为 com.test 的父 logger 就是 root logger,如果之后又定义了一个 name 为 com 的 logger,那么它的父 logger 会更变为新定义的 name 为 com 的 logger,而 name 为 com 的父 logger 就是 root logger。

通过上面介绍,我们 additivity 属性用于配置当前 logger 是否将日志传递给父 logger,默认为传递(true),以上面的例子来说,如果 com.test.logback 包下的类打印了日志,如果会传递到 name 为 com.test.logback 的 logger,并使用它绑定的 appender 来输出日志,又因为它的 additivity 的值为 true,那么当前 logger 在自己输出日志之后,还会将日志传递给父 logger,也就是 name 为 com.test 的 logger,不合理的使用 additivity 属性可能导致重复打印日志的情况。

其中 level 属性也与 logger 的层级结构有关,如果当前 logger 没有定义 level 属性,那么将会从最近的定义了 level 属性的父 logger 中继承,因此 root logger 一般都会设置 level 属性,用于给其他的 logger 统一设置 level 属性。

level 级别:

  • DEBUG:该级别通常用于开发和调试阶段,一般用它来记录详细的程序运行状态信息,比如输入输出、变量值的,这些信息对于排查文件很有帮助,但是在生产环境不建议开启。
  • INFO:该级别用于记录应用程序的重要事件和状态信息,这些信息对于监控和了解应用程序的运行状态非常有用,通常,生成环境会启用该日志级别。
  • WARN:该级别记录潜在的问题和警告信息,这些信息表明应用程序可能会出现问题,但不一定致命。这些日志可以帮助你及时发现潜在的风险。
  • ERROR:该级别用于记录错误事件,例如异常或应用程序中的错误,这些日志通常表明应用程序遇到了问题,需要进行故障排查。
  • FATAL:该级别表示非常严重的错误,通常会导致应用程序无法继续运行。

优先级从高到低依次是:FATAL -> ERROR -> WARN -> INFO -> DEBUG,开启指定的日志级别,会打印该日志级别的日志和所有优先级高于该日志级别的日志,如设置日志级别是 INFO,那么会打印 FATAL,ERROR,WARN 和 INFO 级别的日志,如果设置日志级别是 WARN,那么会打印 FATAL,ERRO 和 WARN 级别的日志。

验证 logger 的层级关系

通过实例来验证上述中 logger 的层级关系,首先创建两个类 com.test.Logback01Testscom.test.logback.Logback02Tests。类定义中省略了 package 和 import 语句。

com.test.Logback01Tests

java 复制代码
public class Logback01Tests {

    private static final Logger log = LoggerFactory.getLogger(Logback01Tests.class);

    public void test01(){

        log.debug("debug");
        log.info("info");

    }
}

com.test.logback.Logback02Tests

java 复制代码
public class Logback02Tests {

    private static final Logger log = LoggerFactory.getLogger(Logback02Tests.class);

    public void test01(){
        log.debug("debug");
        log.info("info");
    }

}

logback.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
       
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
      <encoder>
         <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
      </encoder>
   </appender>

    <logger name="com.test.logback" level="debug" additivity="true">
        <appender-ref ref="STDOUT" />
    </logger>

    <logger name="com.test" level="debug" additivity="true">
        <appender-ref ref="STDOUT" />
    </logger>

   <root level="debug">
   </root>
</configuration>

通过 logback.xml 配置预测日志的输出结果。

因为两个 logger 都设置了 debug 日志级别,所以方法中打印的 debug 和 info 级别两条日志都会打印出来,然后都设置了将日志传递到父 logger 中,则 name 为 com.test.logback 的 logger 会将日志传递到 name 为 com.test 父 logger 中,所以 com.test.logback.Logback02Tests 中的两条日志应该会打印两次,name 为 com.test 的 logger 会将日志传递到 root logger 中,但是 root logger 没有绑定任何的 appender,所以即使传递了也不会打印日志,所以 com.test.Logback01Tests 中的四条日志(加上子 logger 中传递过来的两条日志)应该只会打印一次。

定义一个测试方法调用两个类的方法。

java 复制代码
public class NormalTests {

    @Test
    public void test02(){
        Logback01Tests logback01Tests = new Logback01Tests();
        Logback02Tests logback02Tests = new Logback02Tests();

        logback01Tests.test01();
        logback02Tests.test01();
    }
}

实际输出结果:

bash 复制代码
127  [main] DEBUG com.test.Logback01Tests - debug 
129  [main] INFO  com.test.Logback01Tests - info 
129  [main] DEBUG com.test.logback.Logback02Tests - debug 
129  [main] DEBUG com.test.logback.Logback02Tests - debug 
129  [main] INFO  com.test.logback.Logback02Tests - info 
129  [main] INFO  com.test.logback.Logback02Tests - info 

可以看到输出结果完全符合我们的预期,读者可以通过扩展这个例子来验证其他的一些问题,如将日志级别设置成 INFO 之后的日志输出应该是怎么样的?

root

root 标签用来配置 root logger,它只有一个属性,即 level 属性。与 logger 元素类似,root 元素可以包含零个或多个 <appender-ref> 子标签。

在实际使用中,root logger 通常不会绑定 appender,以避免日志重复打印,日志打印被分配到各个子 logger 中来负责。root logger 中会定义一个 level,以保证每个子 logger 都能继承 level。

完整示例

xml 复制代码
<configuration>
    <!--本文主要输出日志为控制台日志,系统日志,sql日志,异常日志-->
    <!-- %m输出的信息,%p日志级别,%t线程名,%d日期,%c类的全名,,,, -->
    <!--控制台-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d %p (%file:%line\)- %m%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!--系统info级别日志-->
    <!--<File> 日志目录,没有会自动创建-->
    <!--<rollingPolicy>日志策略,每天简历一个日志文件,或者当天日志文件超过64MB时-->
    <!--encoder 日志编码及输出格式-->
    <appender name="fileLog"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>log/file/fileLog.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>log/file/fileLog.log.%d.%i</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- or whenever the file size reaches 64 MB -->
                <maxFileSize>64 MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>
                %d %p (%file:%line\)- %m%n
            </pattern>
            <charset>UTF-8</charset>
            <!-- 此处设置字符集 -->
        </encoder>
    </appender>

    <!--sql日志-->
    <appender name="sqlFile"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>log/sql/sqlFile.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>log/sql/sqlFile.log.%d.%i</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- or whenever the file size reaches 64 MB -->
                <maxFileSize>64 MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!--对记录事件进行格式化。负责两件事,一是把日志信息转换成字节数组,二是把字节数组写入到输出流。-->
        <encoder>
            <!--用来设置日志的输入格式-->
            <pattern>
                %d %p (%file:%line\)- %m%n
            </pattern>
            <charset>UTF-8</charset>
            <!-- 此处设置字符集 -->
        </encoder>
    </appender>


    <!--异常日志-->
    <appender name="errorFile"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>log/error/errorFile.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>log/error/errorFile.%d.log.%i</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- or whenever the file size reaches 64 MB -->
                <maxFileSize>64 MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!--对记录事件进行格式化。负责两件事,一是把日志信息转换成字节数组,二是把字节数组写入到输出流。-->
        <encoder>
            <!--用来设置日志的输入格式-->
            <pattern>
                %d %p (%file:%line\)- %m%n
            </pattern>
            <charset>UTF-8</charset>
            <!-- 此处设置字符集 -->
        </encoder>
        <!--
            日志都在这里 过滤出 error
            使用 try {}catch (Exception e){} 的话异常无法写入日志,可以在catch里用logger.error()方法手动写入日志
            -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <!--  日志输出级别 -->
    <!--All\DEBUG\INFO\WARN\ERROR\FATAL\OFF-->
    <!--打印info级别日志,分别在控制台,fileLog,errorFile输出
        异常日志在上面由过滤器过滤出ERROR日志打印
    -->
    <root level="INFO">
        <appender-ref ref="fileLog" />
        <appender-ref ref="console" />
        <appender-ref ref="errorFile" />
    </root>

    <!--打印sql至sqlFile文件日志-->
    <logger name="com.springboot.demo.mapper" level="DEBUG" additivity="false">
        <appender-ref ref="console" />
        <appender-ref ref="sqlFile" />
    </logger>
</configuration>

SpringBoot 中的日志

SpringBoot 项目的默认日志框架就是 Logback,在 SpingBoot 项目中可以通过 application.properties 来配置日志的打印行为。

properties 复制代码
# 设置logback.xml位置
logging.config=classpath:log/logback.xml

# 为不同的包名设置不同的打印级别
# org.springframework.web 包的打印级别为 debug
logging.level.org.springframework.web=debug
# org.hibernate 包的打印级别为 error
logging.level.org.hibernate=error

# 指定日志文件的名称
logging.file.name=springboot.log
# 指定日志文件的全路径,它包含了指定日志文件名称的功能
logging.file.name=/usr/log/springboot.log

参考:

https://logback.qos.ch/manual/index.html

https://blog.csdn.net/weixin_41377777/article/details/120962037

https://www.cnblogs.com/gavincoder/p/10091757.html

https://juejin.cn/post/6844903841318567949

相关推荐
FF在路上32 分钟前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
真的很上进39 分钟前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
众拾达人1 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言
皓木.1 小时前
Mybatis-Plus
java·开发语言
不良人天码星1 小时前
lombok插件不生效
java·开发语言·intellij-idea
守护者1702 小时前
JAVA学习-练习试用Java实现“使用Arrays.toString方法将数组转换为字符串并打印出来”
java·学习
源码哥_博纳软云2 小时前
JAVA同城服务场馆门店预约系统支持H5小程序APP源码
java·开发语言·微信小程序·小程序·微信公众平台
禾高网络2 小时前
租赁小程序成品|租赁系统搭建核心功能
java·人工智能·小程序
学会沉淀。2 小时前
Docker学习
java·开发语言·学习
如若1232 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python