文章目录
一、 前言
日志框架的选型来说最快的肯定还是log4j2
,TPS能提升6倍多。但是2021年12月10日,log4j2
又被爆出高危漏洞。(前几个月刚开会说用log4j2
,淦!赶紧又换回来了 )
看来不愧是SpringBoot默认的日志实现,logback
还是相对稳定的。
现在还是来详细弄一弄logback
!之前也没有仔细研究过。
项目文件在GitHub(欢迎star⭐):
https://github.com/Gang-bb/Gangbb-SpringBoot
二、使用详解
1. logback介绍
Logback是由log4j创始人设计的另一个开源日志组件。它当前分为下面下个模块:
- logback-core:其它两个模块的基础模块
- logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API使你可以很方便地更换成其它日志系统如log4j或JDK14 Logging
- logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能
Logback
日志级别比较简单,从严重到普通依次是:
- ERROR
- WARN
- INFO
- DEBUG
- TRACE
2. logback配置
版本信息:
Springboot—2.6.1
Logback—1.2.7
2.1 基本配置
SpringBoot工程自带 Logback
和slf4j
的依赖,所以重点放在编写配置文件上,需要引入什么依赖,日志依赖冲突统统都不需要我们管了。
默认的 Logback
配置文件名有两种:
logback.xml
:这种配置文件会直接被日志框架加载。logback-spring.xml
:这种配置文件不会被日志框架直接加载,而是由 Spring Boot 去解析日志配置,可以使用 Spring Boot 的高级 Profile 功能。
如果两个文件都不存在,logback用BasicConfigurator自动对自己进行配置,这会导致记录输出到控制台。
Spring Boot 中为 Logback
提供了四个默认的配置文件,位置在 org/springframework/boot/logging/logback/
,分别是:
- defaults.xml:提供了公共的日志配置,日志输出规则等。
- console-appender.xml:使用 CONSOLE_LOG_PATTERN 添加一个ConsoleAppender。
- file-appender.xml:添加一个 RollingFileAppender。
- base.xml:为了兼容旧版 Spring Boot 而提供的。
可以通过 include 引入 Spring Boot 已经提供的配置文件,也可以自定义。
2.2 Logback 配置项详解
如果需要自定义 logback.xml
文件,可以在自定义时使用这些默认的配置文件,也可以不使用。一个典型的 logback.xml
文件如下(resources/logback.xml):
(1)根节点configuration,包含下面三个属性:
scan
: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。scanPeriod
: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。debug
: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!--...其他配置暂时省略-->
</configuration>
(2)子节点contextName:用来设置上下文名称,每个logger都关联到logger上下文,默认上下文名称为default。但可以使用contextName设置成其他名字,用于区分不同应用程序的记录。一旦设置,不能修改。
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>myAppName</contextName>
<!--...其他配置暂时省略-->
</configuration>
(3)子节点property :用来定义变量值,它有两个属性name和value,通过property定义的值会被插入到logger上下文中,可以使“${}
”来使用变量。
name
: 变量的名称value
: 的值时变量定义的值
例如:
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property name="APP_Name" value="myAppName" />
<contextName>${APP_Name}</contextName>
<!--...其他配置暂时省略-->
</configuration>
(4)子节点timestamp:获取时间戳字符串,他有两个属性key
和datePattern
key
:标识此timestamp 的名字。datePattern
:设置将当前时间(解析配置文件的时间)转换为字符串的模式,遵循java.txt.SimpleDateFormat
的格式。
例如:
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<timestamp key="timestamp_string" datePattern="yyyyMMdd'T'HHmmss"/>
<contextName>${timestamp_string}</contextName>
<!-- 其他配置省略-->
</configuration>
(5)子节点appender:负责写日志的组件,它有两个必要属性name
和class
。
-
name
:指定appender名称 -
class
:指定appender的全限定名,分类ch.qos.logback.core.ConsoleAppender
:把日志输出到控制台,有以下子节点:- < encoder>:对日志进行格式化。(具体参数稍后讲解 )
- < target>:字符串System.out(默认)或者System.err(区别不多说了)
例如:
<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>
上述配置表示把>=DEBUG级别的日志都输出到控制台
-
ch.qos.logback.core.FileAppender
:把日志添加到文件,有以下子节点:- < file>:被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。
- < append>:如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。
- < encoder>:对记录事件进行格式化。
- < prudent>:如果是 true,日志会被安全的写入文件,即使其他的FileAppender也在向此文件做写入操作,效率低,默认是 false。
例如:
<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>
上述配置表示把>=DEBUG级别的日志都输出到testFile.log
-
ch.qos.logback.core.rolling.RollingFileAppender
:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。有以下子节点:-
< file>:被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。
-
< append>:如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。
-
< encoder>:对记录事件进行格式化。负责两件事,一是把日志信息转换成字节数组,二是把字节数组写入到输出流。PatternLayoutEncoder 是唯一有用的且默认的encoder ,有一个节点,用来设置日志的输入格式。使用“%”加“转换符”方式,如果要输出“%”,则必须用“\”对“%”进行转义。
-
< rollingPolicy>:当发生滚动时,决定RollingFileAppender的行为,涉及文件移动和重命名。属性class定义具体的滚动策略类
- < rollingPolicy class=“ch.qos.logback.core.rolling.TimeBasedRollingPolicy”> : 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。有以下子节点:
- < fileNamePattern>:必要节点,包含文件名及“%d”转换符,“%d”可以包含一个java.text.SimpleDateFormat指定的时间格式,如:%d{yyyy-MM}。如果直接使用 %d,默认格式是 yyyy-MM-dd。
- RollingFileAppender的file字节点可有可无,通过设置file,可以为活动文件和归档文件指定不同位置,当前日志总是记录到file指定的文件(活动文件),活动文件的名字不会改变;如果没设置file,活动文件的名字会根据fileNamePattern 的值,每隔一段时间改变一次。“/”或者“\”会被当做目录分隔符。
- < maxHistory>:可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每个月滚动,且< maxHistory>是6,则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除。
例如:
<configuration> <appender name="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="FILE" /> </root> </configuration>
备注:上述配置表示每天生成一个日志文件,保存30天的日志文件。
- < rollingPolicy class=“ch.qos.logback.core.rolling.FixedWindowRollingPolicy”> :根据固定窗口算法重命名文件的滚动策略。有以下子节点:
- < minIndex>:窗口索引最小值
- < maxIndex>:窗口索引最大值,当用户指定的窗口过大时,会自动将窗口设置为12
- < fileNamePattern>:必须包含“%i”例如,假设最小值和最大值分别为1和2,命名模式为 mylog%i.log,会产生归档文件mylog1.log和mylog2.log。还可以指定文件压缩选项,例如,mylog%i.log.gz 或者 没有log%i.log.zip
- < triggeringPolicy class=“ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy”> :查看当前活动文件的大小,如果超过指定大小会告知RollingFileAppender 触发当前活动文件滚动。只有一个节点:
- < triggeringPolicy >: 告知 RollingFileAppender 合适激活滚动。
- < maxFileSize>:这是活动文件的大小,默认值是10MB。
- < prudent>:当为true时,不支持FixedWindowRollingPolicy。支持TimeBasedRollingPolicy,但是有两个限制,1不支持也不允许文件压缩,2不能设置file属性,必须留空。
<configuration> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>test.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> <fileNamePattern>tests.%i.log.zip</fileNamePattern> <minIndex>1</minIndex> <maxIndex>3</maxIndex> </rollingPolicy> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <maxFileSize>5MB</maxFileSize> </triggeringPolicy> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="FILE" /> </root> </configuration>
- < rollingPolicy class=“ch.qos.logback.core.rolling.TimeBasedRollingPolicy”> : 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。有以下子节点:
-
备注:上述配置表示按照固定窗口模式生成日志文件,当文件大于5MB时,生成新的日志文件。窗口大小是1到3,当保存了3个归档文件后,将覆盖最早的日志。
- 还有SocketAppender、SMTPAppender、DBAppender、SyslogAppender、SiftingAppender,并不常用
(6)子节点logger:用来设置某一个包或具体的某一个类的日志打印级别、以及指定< appender>。< logger>仅有一个name属性,一个可选的level和一个可选的addtivity属性。可以包含零个或多个< appender-ref>元素,标识这个appender将会添加到这个logger
-
name
:用来指定受此loger约束的某一个包或者具体的某一个类。 -
level
:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。如果未设置此属性,那么当前loger将会继承上级的级别。 -
addtivity
:是否向上级loger传递打印信息。默认是true。
(7)子节点root:它也是< logger>元素,但是它是根logger,是所有< logger>的上级。只有一个level属性,因为name已经被命名为"root",且已经是最上级了。level: 用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL和OFF,不能设置为INHERITED或者同义词NULL。 默认是DEBUG。
2.3 Logback配置打印信息着色
配置文件片段截取:
<!-- 控制台输出格式 -->
<property name="console.log.pattern" value="%red(%d{yyyy-MM-dd HH:mm:ss.SSS}) %green([%thread]) %highlight(%-5level) %boldMagenta([%logger{36}]) - %cyan(%msg%n)" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${console.log.pattern}</pattern>
</encoder>
</appender>
官方文档关于该部分介绍:
http://logback.qos.ch/manual/layouts.html#coloring
支持颜色:
其他自定义颜色貌似是支持的,自行查看文档或者谷歌百度!
注意事项:
-
第一点,颜色%black %red等等 ,需要用括号将你要显示本颜色的子模块括起来
-
第二点,%red颜色等,前面要与上一个模块 空格隔开
示例打印着色效果:
PS:虽然有点花里胡哨的,但是个人感觉比原来的白白一片看着舒服多了~
2.4 不同环境配置不同的日志级别
方法一:读取 spring 配置文件中的值
logback.xml 早于 application.yml 加载,logback-spring.xml 晚于 application.yml 加载,如果 logback 配置需要使用 application.yml 中的属性,需要命名为 logback-spring.xml。
现在采用 logback-spring.xml 文件来读取配置文件 application-${profile}.properties 中的属性值。logback 需要使用 标签才可使用 application.properties 中的属性,当配置文件中不存在属性值时,也可以设置默认值,示例如下所示。
<!-- 读取 application.properties 中的 log.level 属性,如果没有配置,默认 INFO -->
<springProperty name="LOG_LEVEL" source="log.level" defaultValue="INFO"/>
<logger name="com.test.svc" level="${LOG_LEVEL}" additivity="false">
<appender-ref ref="ASYNC"/>
<appender-ref ref="STDOUT"/>
</logger>
方法二:加载指定的配置模块
如下配置中,< springProfile name=“dev”> 中的 name,读取的是 spring.profiles.active 配置项的值,不设置默认为 default。
如下两条的意思是,当 spring.profiles.active 配置为 dev 的时候,包裹在其中的部分配置被加载。spring.profiles.active 没有配置,或者配置为 default 的时候,包裹在其中的日志设置内容被加载。如果有其他环境 test、prod、release 等,仿照如此配置即可。name 后面还可以跟表达式。
<springProfile name="dev">
<!-- 开发环境时激活 -->
<logger name="com.test.svc" level="DEBUG" additivity="false">
<appender-ref ref="ASYNC"/>
<appender-ref ref="STDOUT"/>
</logger>
</springProfile>
<springProfile name="default">
<!-- 默认环境时激活 -->
<logger name="com.test.svc" level="INFO" additivity="false">
<appender-ref ref="ASYNC"/>
<appender-ref ref="STDOUT"/>
</logger>
</springProfile>
2.5 logback 高级特性异步输出日志
之前的日志配置方式是基于同步的,每次日志输出到文件都会进行一次磁盘IO。采用异步写日志的方式而不让此次写日志发生磁盘IO,阻塞线程从而造成不必要的性能损耗。异步输出日志的方式很简单,添加一个基于异步写日志的appender,并指向原先配置的appender即可
<!-- 异步输出 -->
<appender name="ASYNC-INFO" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>256</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="INFO-LOG"/>
</appender>
<appender name="ASYNC-ERROR" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>256</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="ERROR-LOG"/>
</appender>
2.6 完整配置示例
Spingboot项目下的resources/logback-spring.xml
文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<!-- 一、根节点<configuration>,包含下面三个属性-->
<!--1.scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true-->
<!--2.scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。-->
<!--3.debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。-->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 日志存放路径 -->
<property name="log.path" value="./logback/logs" />
<!-- 控制台输出格式 -->
<property name="console.log.pattern" value="%red(%d{yyyy-MM-dd HH:mm:ss.SSS}) %green([%thread]) %highlight(%-5level) %boldMagenta([%logger{60}]) - %cyan(%msg%n)" />
<!-- 文件输出格式 -->
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level [%logger{36}] - %msg%n"/>
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${console.log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统 INFO 日志文件输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!--DENY:表示不用看后面的过滤器了,这里就给拒绝了,不作记录。-->
<!--NEUTRAL:表示需不需要记录,还需要看后面的过滤器。若所有过滤器返回的全部都是NEUTRAL,那么需要记录日志。-->
<!--ACCEPT:表示不用看后面的过滤器了,这里就给直接同意了,需要记录。-->
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 系统 error 日志文件输出 -->
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--*********异步输出日志配置*********-->
<!-- 异步输出 -->
<appender name="ASYNC-INFO" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>256</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="file_info"/>
</appender>
<appender name="ASYNC-ERROR" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>256</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="file_error"/>
</appender>
<!--*********设置一些包的打印级别,过滤一些无用信息*********-->
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="info" />
<logger name="_org.springframework" level="info" />
<!-- 默认环境日志级别 -->
<springProfile name="default">
<!--日志文件打印日志级别-->
<root level="debug">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
<appender-ref ref="console" />
</root>
</springProfile>
<!-- 开发环境日志级别 -->
<springProfile name="dev">
<!--日志文件打印日志级别-->
<root level="debug">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
<appender-ref ref="console" />
</root>
</springProfile>
<!-- 测试环境日志级别 -->
<springProfile name="test">
<!--日志文件打印日志级别-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
<appender-ref ref="console" />
</root>
</springProfile>
<!-- 线上环境日志级别 -->
<springProfile name="prod">
<!--日志文件打印日志级别-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
<appender-ref ref="console" />
</root>
</springProfile>
</configuration>
项目启动后打印:
如果命名为logback.xml
,启动项目会打印logback的加载配置日志:
如此配置的文件保存效果: