目录
前言
- 思路:官网学习,官网有大量的配置例子,并且都会给出解释
- 需要前置知识:了解 Spring Boot 的依赖结构、门面模式
- 最好了解:Spring MVC 的工作原理,可以类比理解
slf4j
- 学习demo放到文末了(github)
Logback 的前世今生
log4j -> jul -> slf4j -> logback -> log4j2
简单来说,log4j是第一款日志框架,jdk看这个框架反响不错,就在jdk中内置了java.util.logging这个包提供日志功能。当时有两个日志框架在java开发界并存,如果项目A刚开始使用了log4j
,需要跟另外个项目B融合,项目B使用的是jul
。不同的日志框架的编码风格可能会造成混乱,于是乎遵循软件工程沉淀的门面模式经验,诞生了 slf4j
这个框架。
-
可以这么理解:
slf4j
作为所有日志框架的前置过滤器,类似 Spring MVC 的 DispatcherServlet,log4j、jul 则是不同的view。这么设计的好处是,不论view如何变化,对客户端提供的请求接口不变、model也是不变的。 -
logback
和log4j2
这两个框架都诞生在slf4j
之后,完全遵循slf4j
的接口规范。 -
为什么选择
logback
本文只是因为这是 Spring Boot 的内置依赖
一个基础的配置
<configuration>
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
解释及理解
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
- 打印日志框架本身的运行信息,非常有用,框架启动异常往往就能定位问题
- 效果
- 效果
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
- root标签本质代表的是一个
Logger
,但是这个Logger
的名称是顶级的,必须为名称为root - 日志等级
level
error > warn > info > debug > trace
- 一个
Logger
下面可以挂载N个appender
。- 为什么可以挂载N个?
log.info(“hello") 即要打印到控制台又要打印到日志文件,那么就需要控制台的appender和日志文件的appender
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
- 声明一个 Appender
- 打印日志时候的前缀格式可以自定义,更多自定义的选项可以参考官网
需求:某个类/某个包使用特殊的日志等级
<!--顶级是debug日志; 这里也可以设置包名-->
<logger name="com.james.usinglog.InfoTests" level="INFO"/>
<logger name="com.james.usinglog.TraceTests" level="TRACE"/>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
值得注意的是,获取Logger时候,使用的.class文件一定要对应当前类
解释及理解
上文提到 root 是一个特殊的 logger
, 也就是所有包的打印都默认继承一个叫 root
的 logger
。
如果要针对某个类或者包覆盖掉父类的实现,非常简单,提供自己的实现即可。所以定义一个logger
并声明一个独有的 level
就能实现需求。官网对这个继承体系提供例子帮助我们理解:
需求:写入日志文件
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logFile.log</file>
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
解释及理解
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
- 新建一个appender,使用logback 的 ch.qos.logback.core.FileAppender 实现
<file>logFile.log</file>
- 日志文件的相对路径,当然也可以改为绝对路径
<root level="debug">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
- 把新的appender加进logger的结构中
需求:有多个Appender时,只想用其中一个
<!--该类(也可以写包名)不让root接管, 所以这个类的日志不会打印到控制台-->
<logger name="com.james.usinglog.FileOnlyTests" additivity="false">
<appender-ref ref="FILE" />
</logger>
<root level="debug">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
解释及理解
上文提到了 logger 的继承体系,深层次来说,他们的Appender也是继承的。怎么理解?如果一个包有自己的logger,它默认继承了root,则自己的Logger和root的logger都会被触发。这个特性使我们配置一个新logger加入到 root标签,就能做到同时往控制台和文件打印日志。
- 取一个逆向思维,只让自己的logger生效怎么办?
<logger name="com.james.usinglog.FileOnlyTests" additivity="false">
打破继承体系即可,仅仅声明使用自己的 additivity=“false”
需求:日志Rolling (按文件滚动增量增加)
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>rollingLogFile.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 表达式有两个作用 1.确认滚动增加的最小单位 2. 文件格式 -->
<fileNamePattern>rollingLogFile.%d{yyyy-MM-dd_HH-mm}.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
<appender-ref ref="ROLLING_FILE" />
</root>
值得注意的是: class=“ch.qos.logback.core.rolling.RollingFileAppender” 一定要区分与普通的
FileAppender 实现,不然加载配置文件会报错
解释及理解
仅仅是使用一个logback 的 Appender实现。关键是理解表达式 %d{yyyy-MM-dd_HH-mm}
的副作用。官网明确说明是有两个副作用:
1. 确认文件名格式
2. 以最小单位进行分片。EG:{yyyy-MM-dd_HH-mm}
就是以分钟为单位进行分片,如:
更多表达式可以参考官网
后记
理解了 logger 的继承体系和 appender 的具体实现后,组织自己的日志策略就相对简单了。诚然,生产环境的配置更加复杂,包括引入了邮件预警等实现,这个需要使用到其他包,后续有机会会研究这个东西。
附上本文练习用的github地址:
https://github.com/ChenghanY/usinglog