简介
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.Logback01Tests
和 com.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