简单使用
创建一个 SpringBoot 项目即可直接使用 Logback 进行日志打印,只需引入 spring-boot-starter-web
:
在依赖 spring-boot-starter-logging
都已经引用好了,随后直接启动项目:
这里仅仅只是创建了一个 SpringBoot 项目,什么都没有配置,启动之后,日志也已经存在了。仔细观察可以看出打印的日志是具有同一格式的,依次是日期、时间、日志级别、进程号(PID)、线程名、包路径、具体日志消息。
至于为什么默认实现是 Logback,有兴趣可以看这里:
org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationStartingEvent
->org.springframework.core.io.support.SpringFactoriesLoader#loadFactories
默认配置
那既然什么都没有配置就有上面的格式,说明 SpringBoot 做了一些默认化的自动配置。在方法 org.springframework.boot.logging.AbstractLoggingSystem#initializeWithConventions
进行了默认日志的配置:
- 在
getSelfInitializationConfig
中会尝试找到logback-test.groovy
、logback-test.xml
、logback.groovy
、logback.xml
其中一个作为配置文件,默认情况下一个都找不到,因为资源目录并没有对应的配置文件。 - 1 里什么都没找到,所以就走到了这里,在
getSpringInitializationConfig
中会把 1 中的四个文件名都拼凑上-spring
然后再尝试去找到对应的配置文件,比如logback.xml
变成logback-spring.xml
。默认情况下也是一个都找不到,因为资源目录并没有对应的配置文件。 - 1 和 2 中只要有一个能找到,那么就回到 3 中进行出日志的初始化配置。
- 载入默认日志配置,也就是默认项目启动的日志打印方式,最终在
org.springframework.boot.logging.logback.DefaultLogbackConfiguration#defaults
处加载默认配置。
日志配置
日志级别
TRACE < DEBUG < INFO < WARN < ERROR,使用的日志级别越小,打印的日志越多,比如设置的日志级别是
DEBUG
,那么项目中的所有大于DEBUG
的日志都会打印。如果设置的日志级别是ERROR
,那其他四个日志就都不会打印。
- TRACE:级别最小的日志,所有的日志都进行打印,一般不使用。
- DEBUG:此日志级别一般用作调试使用,比如新开发的功能,还不是太放心,就开启此日志级别。
- INFO: 最常使用的日志级别,比如打印产生分支的信息,比如一个 if 的条件,让开发能通过日志排查问题。
- WARN:警告日志,代码走到比较让人需要注意的地方打印此级别。
- ERROR:发生错误时打印。
借官方的一张图:
横排是项目配置的日志级别。竖排是项目中打印日志使用的日志级别。
日志格式
logback-chinese-manual/06第六章:Layouts
这里是从 YLongo/logback-chinese-manual: logback 中文手册/文档 Fork 了一份。
配置文件结构
一般配置方式(并不是)如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true" scan="true" scanPeriod="30 minutes">
<!-- 控制台颜色 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<!-- 控制台输出日志 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS} - %clr(%-5level)] %20thread %clr([%-50logger{49}]){cyan} : %msg%n</pattern>
</encoder>
</appender>
<!-- 文件输出日志 (文件大小策略进行文件输出,超过指定大小对文件备份) -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/log/server/example/项目名称.server.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>/log/server/example/项目名称.server_log.%i.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>21</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>500MB</maxFileSize>
</triggeringPolicy>
<encoder>
<Pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS} - %-5level] %20thread [%-50logger{49}] : %msg%n</Pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
<logger name="com">
<level value="INFO"/>
<appender-ref ref="FILE"/>
<appender-ref ref="STDOUT"/>
</logger>
</configuration>
整体结构
上面是一个比较全面的结构,但是往往常用的也就那么几个,如下:
configuration
根节点 configuration
,其中包括了以下几个属性:
- scan:当此属性设置为 true 时,配置文件如果发生改变,将会被重新加载,默认值为 true。
- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位:毫秒。当 scan 为 true 时,此属性生效。默认的时间间隔:1分钟。
- debug:当此属性设置为 true 时,将打印出 logback 内部日志信息,实时查看 logback 运行状态。默认值为 false。
appender
子节点 appender
,主要负责写日志的组件,具有两个属性:
- name:起个名字,方便别处使用。
- class:指定一个具体的
Appender
实现,此实现方式影响日志输出的目的地,比如是控制台、指定文件等。知道一些常用的就行了。
属性:class = ch.qos.logback.core.ConsoleAppender
Appender 的一种实现,作用是把日志输出到控制台。
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS} - %clr(%-5level)] %20thread %clr([%-50logger{49}]){cyan} : %msg%n</pattern>
</encoder>
</appender>
- target:输出到控制台的方式,如下两种:
- System.out(默认)
- System.err
- encoder:对要输出的日志格式化使用,在
0.9.19
引进 encoder 之前使用 layout。
属性:class = ch.qos.logback.core.FileAppender
Appender 的一种实现,作用是把日志输出到指定文件中。
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>/log/server/example/项目名称.server.log</file>
<append>true</append>
<prudent>false</prudent>
<bufferSize>8192</bufferSize>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS} - %-5level] %20thread [%-50logger{49}] : %msg%n</pattern>
</encoder>
</appender>
- file:要写入的文件绝对路径,这里的路径没有设置盘符,Linux 和 Mac 好理解,如果是 Windows,那即是当前项目所在盘符根目录开始。
- append:是否追加写入,默认值:true。
- prudent:当设置为 true 时,来自多个 jvm 的文件追加器可以安全地写入同一个文件。默认值:false。
- bufferSize:缓冲区大小,单位:字节,默认值:8192。
- encoder:对要输出的日志格式化使用,在
0.9.19
引进 encoder 之前使用 layout。
属性:class = ch.qos.logback.core.rolling.RollingFileAppender
Appender 的一种实现,作用是把日志输出到指定文件中,当文件达到一定规则时,在触发规则时会做不同的事,此规则受 rollingPolicy
和 triggeringPolicy
的影响。
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/log/server/example/项目名称.server.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>/log/server/example/项目名称.server_log.%i.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>21</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>500MB</maxFileSize>
</triggeringPolicy>
<encoder>
<Pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS} - %-5level] %20thread [%-50logger{49}] : %msg%n</Pattern>
</encoder>
</appender>
- file:要写入的文件绝对路径,这里的路径没有设置盘符,Linux 和 Mac 好理解,如果是 Windows,那即是当前项目所在盘符根目录开始。
- rollingPolicy:滚动策略。
- triggeringPolicy:触发策略。
- encoder:对要输出的日志格式化使用,在
0.9.19
引进 encoder 之前使用 layout。
rollingPolicy:class = ch.qos.logback.core.rolling.FixedWindowRollingPolicy
当 triggeringPolicy
触发时,日志文件开始按照 minIndex
到 maxIndex
进行分割。
- fileNamePattern:文件名称格式。
%i
是在触发triggeringPolicy
时递增用来命名文件使用。 - minIndex:起始值,比如:1。
- maxIndex:结束值,比如:20。当 maxIndex < minIndex 时,maxIndex = minIndex。
理论上 maxIndex - minIndex 不能超过 20,如果 minIndex 为:1,maxIndex 最多只能为:21。如下图:
最终物理存储日志的结构是:
rollingPolicy:class = ch.qos.logback.core.rolling.TimeBasedRollingPolicy
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- <file>/log/server/example/项目名称.server.log</file>-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/log/server/example/项目名称_%d{yyyy-MM-dd_HH-mm}.log</fileNamePattern>
<!-- 日志在磁盘上保留的单位数 -->
<maxHistory>10</maxHistory>
<cleanHistoryOnStart>fasle</cleanHistoryOnStart>
</rollingPolicy>
<!--<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>1KB</maxFileSize>
</triggeringPolicy>-->
<encoder>
<Pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS} - %-5level] %20thread [%-50logger{49}] : %msg%n</Pattern>
</encoder>
</appender>
无需 triggeringPolicy
的触发,appender
下的 file
也不需要使用,否则会报错。
- fileNamePattern:日志名称,可以设置日期形式。
- maxHistory:最多存放多少个历史日志,如果当前的纬度是按照天为单位,那
maxHistory
天前的日志将会删除,如果是分钟,那么maxHistory
分钟前的日志将会删除,以此类推。 - cleanHistoryOnStart:在启动时,是否清理历史日志,默认:false。
rollingPolicy:class = ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- <file>/log/server/example/项目名称.server.log</file>-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>/log/server/example/项目名称_%d{yyyy-MM-dd_HH}-%i.log</fileNamePattern>
<!-- 日志在磁盘上保留的单位数 -->
<maxHistory>10</maxHistory>
<cleanHistoryOnStart>fasle</cleanHistoryOnStart>
<maxFileSize>1KB</maxFileSize>
</rollingPolicy>
<!--<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>1KB</maxFileSize>
</triggeringPolicy>-->
<encoder>
<Pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS} - %-5level] %20thread [%-50logger{49}] : %msg%n</Pattern>
</encoder>
</appender>
SizeAndTimeBasedRollingPolicy
是 TimeBasedRollingPolicy
的子类,具有 TimeBasedRollingPolicy 的特性,新增了一个属性 maxFileSize
,在 TimeBasedRollingPolicy 的基础上,增加一个文件大小的判断,fileNamePattern
上要增加 %i
的配置。
在相应的一个时间单位纬度内,会根据日志文件大小使用 %i
参数来进行切割日志文件。举例如下:
为什么 SizeAndTimeBasedRollingPolicy
和 TimeBasedRollingPolicy
不需要配置 triggeringPolicy
也可正常运行,可以看下这两个类的结构:
他们都实现了 ch.qos.logback.core.rolling.RollingPolicy
和 ch.qos.logback.core.rolling.TriggeringPolicy
接口,再看 ch.qos.logback.core.rolling.RollingFileAppender
里面的 set 方法:
发现随便设置哪一个,其实结果都是一样的。
TimeBasedRollingPolicy
还有一个属性 timeBasedFileNamingAndTriggeringPolicy
,可以通过如下方式配置:
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- <file>/log/server/example/项目名称.server.log</file>-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/log/server/example/项目名称_%d{yyyy-MM-dd_HH-mm}.log</fileNamePattern>
<!-- 日志在磁盘上保留的单位数 -->
<maxHistory>10</maxHistory>
<cleanHistoryOnStart>fasle</cleanHistoryOnStart>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>1KB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!--<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>1KB</maxFileSize>
</triggeringPolicy>-->
<encoder>
<Pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS} - %-5level] %20thread [%-50logger{49}] : %msg%n</Pattern>
</encoder>
</appender>
SizeAndTimeBasedRollingPolicy
的具体实现也是利用了这一点,如下:
triggeringPolicy:class = ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/log/server/example/${spring.application.name}.server.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>/log/server/example/${spring.application.name}.server_log.%i.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>21</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>500MB</maxFileSize>
</triggeringPolicy>
<encoder>
<Pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS} - %-5level] %20thread [%-50logger{49}] : %msg%n</Pattern>
</encoder>
</appender>
- maxFileSize:当单个日志文件大小达到设定值时,将进行文件滚动策略。
一般最少都会做两个
appender
,ConsoleAppender
和RollingFileAppender
,RollingFileAppender 的看自己情况来定。
logger
属性
- name:指定某个包下或者具体类的日志级别。比如:com、org、io、java.lang.String等
- level:用来设置打印级别,大小写无关:ALL、TRACE、DEBUG、INFO、WARN、ERROR、OFF,还有一个特殊值
INHERITED
或者同义词NULL
,代表强制执行上级的级别。 如果未设置此属性,那么当前 logger 将会继承上级的级别。 - addtivity:是否接受叠加上级配置的打印信息。默认:true,下面会详说。
子标签
- appender-ref:用来指定
<appender/>
使用,比如上面的STDOUT
、FILE
,<logger>
标签用来控制日志的输出级别,左右日志是否进行输出,并使用字标签appender-ref/>
指定对应的 appender 来进行输出。
additivity
是否接受叠加上级配置的打印信息,先看一个配置:
对应的打印信息:
日志的打印级别控制在了 INFO 级别,但是打印了两次。再加一个配置:
打印如下:
现在把 name="com.lynchj"
的 additivity
改为 false ,name="com"
的 additivity
还是 true,打印如下:
由此得出结论:日志是否打印受最近的(logger 标签中配置的 name 距离打印日志类的距离)一个 logger 级别控制,而日志是否会重复打印要看当前打印日志的类全路径上是否配置多个 logger,是否有开启 additivity,只要最近的一个 logger 配置没有开启 additivity 就不会重复打印。
root
作用与 logger 一般无二,只是它没有 name、additivity 两个属性,没有 name 是因为它代表的就是根目录,所以不需要,根目录自然也不需要使用 additivity 进行叠加。
contextName
用来设置上下文名称,每个 logger 都关联到 logger 上下文,默认上下文名称为 default
。可以通过此标签设置成其他名字,用于区分不同应用程序的记录。
property
<property name="CONSOLE_LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS} - %clr(%-5level)] %20thread %clr([%-50logger{49}]){cyan} : %msg%n"/>
<!-- 控制台颜色 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<!-- 控制台输出日志 -->
<appender name="CONSOLE_LOG" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
定义变量使用。
conversionRule
定义一些规则,比如控制台输出日志的颜色、固定位置打印当前机器的 IP 等。
带颜色打印
<property name="CONSOLE_LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS} - %clr(%-5level)] %20thread %clr([%-50logger{49}]){cyan} : %msg%n"/>
<!-- 控制台颜色 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<!-- 控制台输出日志 -->
<appender name="CONSOLE_LOG" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
在 pattern 中就可以使用 %clr(内容){颜色}
来改变要输出内容的颜色,颜色可以通过 org.springframework.boot.logging.logback.ColorConverter
查看。
打印 IP
自定义一个转换器:
public class LocalIpConfig extends ClassicConverter {
private static volatile String localRationalIp;
@Override
public String convert(ILoggingEvent event) {
try {
if (null == localRationalIp) {
localRationalIp = IpUtils.getLocalRationalIp();
}
return localRationalIp;
} catch (SocketException e) {
log.error("【支持 LogBack 获取本地 IP 信息】- 出错了:${}$", ExceptionUtils.getStackTrace(e));
}
return null;
}
}
<-- IP -->
<conversionRule conversionWord="ip" converterClass="com.car2p.p600.config.log.LocalIpConfig" />
<!-- 控制台颜色 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<property name="CONSOLE_LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS} - %ip - %clr(%-5level)] %20thread %clr([%-50logger{49}]){cyan} : %msg%n"/>
<!-- 控制台输出日志 -->
<appender name="CONSOLE_LOG" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
之后就可以在 pattern 中使用 %ip
进行答应。
结合 SpringBoot 的扩展项
写在前面,一个注意事项,一个警告。
- 注意事项:因为标准的
logback.xml
配置文件加载得太早,所以不能在其中使用扩展。需要使用logback-spring.xml
或定义logging.config
属性。否则不会生效。 -
警告:扩展项标签不能与 configuration scanning 共同使用,会使
configuration scanning
无效,并抛出类似以下错误:
ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProperty], current ElementPath is [[configuration][springProperty]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProfile], current ElementPath is [[configuration][springProfile]]
扩展 - springProfile
<springProfile name="dev">
<-- spring.profiles.active = dev 时,root 根目录日志级别为:DEBUG -->
<root level="DEBUG">
<appender-ref ref="CONSOLE_LOG" />
<appender-ref ref="FILE_LOG" />
</root>
</springProfile>
<springProfile name="dev">
<-- spring.profiles.active = dev 时,io 目录日志级别为:DEBUG -->
<logger name="io" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE_LOG"/>
<appender-ref ref="FILE_LOG"/>
</logger>
</springProfile>
<springProfile name="dev | test">
<-- spring.profiles.active = dev 或者 test 时,com 目录日志级别为:INFO -->
<logger name="com" level="INFO" additivity="false">
<appender-ref ref="CONSOLE_LOG"/>
<appender-ref ref="FILE_LOG"/>
</logger>
</springProfile>
<springProfile name="!pro">
<-- spring.profiles.active != pro 时,org 目录日志级别为:INFO -->
<logger name="org" level="INFO" additivity="false">
<appender-ref ref="CONSOLE_LOG"/>
<appender-ref ref="FILE_LOG"/>
</logger>
</springProfile>
同样的,它也可以作用在其他标签上,如下:
扩展 - springProperty
The
source
must be specified in kebab case (such asmy.property-name
). However,properties
can be added to theEnvironment
by using the relaxed rules.
该标签允许把 Spring 内部管理的属性暴露给日志文件使用。如果想在日志备份配置中访问 Spring 中的一些变量,可以使用此标签。样例如下:
<springProperty scope="context" name="PROJECT_LEVEL" source="logback.project.level" defaultValue="DEBUG"/>
<springProperty scope="context" name="SPRING_LEVEL" source="logback.spring.level" defaultValue="DEBUG"/>
<springProperty scope="context" name="APPLICATION_NAME" source="spring.application.name" defaultValue="spring-boot"/>
之后只需要通过 Maven Profile 来调整 SpringBoot 的 profile 即可根据不同环境打印不同级别的日志。
总结
类中的日志打印可以是 TRACE、DEBUG、INFO、WARN、ERROR 等,是否打印取决于 logger 配置的路径是否包含(如果配置了 root 标签,那肯定会包含,至少 root 会包含,如果没有配置 root,logger 也没有一个包含的,那日志永远不会打印)对应的类,并且 logger 配置的日志级别需要小于类中打印的级别,否则无法打印。
是否叠加输出是取决于两方面:
- 同一条路径(类路径)上是否存在多个配置,比如:name=com、name=com.lynchj 就算是相同路径有两个配置,那在
com.lynchj
包下(包括子包)的所有类都相当于有两个配置。如果配置了 root 标签,因为 root 代表了是根目录,所以所有类默认都有了一个配置,如果再主动配置 logger 标签指明包路径,那就是存在多个配置。 - additivity 默认:true,允许叠加,设置为 false 不会进行叠加。如果存在两个相同的路径 logger 配置,比如:name=com、name=com,additivity 会覆盖(下面覆盖上面的)、appender-ref 会追加,如果下边的 additivity 没有明确配置而上边的明确配置了,则不会覆盖上面的
在确定了日志是否输出、是否叠加之后,就是要使用 appender-ref
把日志输出出去了,是输出到控制台、文件,是滚动输出或者一直追加输出等就看对应的 appender 如何配置了。
文章评论