Logback最佳实践
Logback架构
Logback分为三个模块:Logback-core,Logback-classic和Logback-access。
Logback-core是其他两个模块实现的基础。Logback-classic相当于是Log4j的改进版,并且实现了Slf4J接口。Logback-access与Servlet容器集成,以提供HTTP访问日志功能(这个模块实际开发中很少用到)。
Logback核心对象
Logback有三个核心对象:Logger、Appender、Layout。
Logger 位于 logback-classic 模块中, 而 Appender 和 Layout 位于 logback-core 中,这意味着, Appender 和 Layout 并不关心 Logger 的存在,不依赖于 Logger,同时也能看出, Logger 会依赖于 Appender 和 Layout 的协助,日志信息才能被正常打印出来。
- Logger:日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。
- Appender:主要用于指定日志输出的目的地,目的地可以是控制台、文件、远程套接字服务器、 数据库、 JMS和远程UNIX Syslog守护进程等。
- Layout:把事件转换成字符串,格式化的日志信息的输出。
Logger
各个Logger 都被关联到一个 LoggerContext,LoggerContext负责制造Logger ,也负责以树结构排列各Logger 。其他所有logger也通过org.slf4j.LoggerFactory 类的静态方法getLogger取得。 getLogger方法以 Logger 名称为参数。用同一名字调用LoggerFactory.getLogger 方法所得到的永远都是同一个Logger 对象的引用。
层次命名规则
为了可以控制哪些信息需要输出,哪些信息不需要输出,Logback中引进了一个 分层 概念。每个 logger 都有一个 name,这个 name 的格式与 Java 语言中的包名格式相同。Logger 的 name 格式决定了多个 Logger 能够组成一个树状的结构,为了维护这个分层的树状结构,每个 logger 都被绑定到一个 Logger 上下文中,这个上下文负责厘清各个 logger 之间的关系。
例如,"com.foo"
是"com.foo.Bar"
的父Logger。同样, "java"
是"java.util"
的父Logger和"java.util.Vector"
祖先Logger。
root Logger是所有Logger的祖先,这是 logback 内部维护的一个 logger,并非开发者自定义的 Logger 。
Logger有日志打印级别继承规则,对于一个未设置打印级别的Logger会向上寻找一个设置了日志打印级别的父Logger,直到root Logger。因此 必须为 root Logger指定日志打印级别。
Appender & Layout
Appender 是绑定在 logger 上的,同时,一个 logger 可以绑定多个 Appender,意味着一条信息可以同时打印到不同的目的地去。例如,常见的做法是,日志信息既输出到控制台,同时也记录到日志文件中,这就需要为 logger 绑定两个不同的 logger。
logger 有继承关系,因此一个 logger 打印信息时的目的地 Appender 需要参考它的父亲和祖先。在 logback 中,默认情况下,如果一个 logger 打印一条信息,那么这条信息首先会打印至它自己的 Appender,然后打印至它的父亲和父亲以上的祖先的 Appender,但如果它的父亲设置了 additivity = false
,那么这个 logger 除了打印至它自己的 Appender 外,只会打印至其父亲的 Appender,因为它的父亲的 additivity
属性置为了 false,开始变得忘祖忘宗了,所以这个 logger 只认它父亲的 Appender;此外,对于这个 logger 的父亲来说,如果父亲的 logger 打印一条信息,那么它只会打印至自己的 Appender中(如果有的话),因为父亲已经忘记了爷爷及爷爷以上的那些父辈了。
打印的日志除了有打印的目的地外,还有日志信息的展示格式。在 logback 中,用 Layout 来代表日志打印格式。比如说,PatternLayout 能够识别以下这条格式:
%-4relative [%thread] %-5level %logger{32} - %msg%n
然后打印出来的格式效果是:
176 [main] DEBUG manual.architecture.HelloWorld2 - Hello world.
第一个字段是自程序启动以来经过的毫秒数。第二个字段是发出日志请求的线程。第三个字段是日志打印级别。第四个字段是Logger名称。“-”之后的文本是日志消息。
Logback加载流程
- Logback尝试在classpath下找到一个名为logback-test.xml 的文件 。(xml配置)
- 如果找不到上述文件,尝试在classpath找到一个名为logback.groovy 的文件 。(groovy配置)
- 如果找不到上述文件,尝试在classpath中找到一个名为logback.xml的文件。
- 如果上述的文件都找不到,则 logback 会使用 JDK 的 SPI 机制查找 META-INF/services/ch.qos.logback.classic.spi.Configurator 中的 logback 配置实现类,这个实现类必须实现
Configuration
接口,使用它的实现来进行配置。(编程式配置) - 如果以上方法均未成功,则Logback将使用自带的
BasicConfigurator
进行自动配置。该配置构造一个ConsoleAppender
用于向控制台输出日志,默认日志输出格式为”%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
“,日志打印级别为Debug。
在Maven工程中,一般将 logback-test.xml放在src / test / resources 文件夹下,Maven将确保它不会包含在生成的工件中。因此,您可以在测试期间使用其他配置文件,即logback-test.xml,在生产环境中使用另一个文件,即logback.xml。
logback 启动的时候解析配置文件大概需要 100 毫秒的时间,如果希望更快启动,可以采用 SPI 的方式。
Logback的默认配置
BasicConfigurator
默认配置可以使用以下配置文件表述:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<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>
Logback配置文件语法
配置文件的最基本结构可以描述为<configuration>
元素,包含零个或多个<appender>
元素,然后是零个或多个<logger>
元素,然后是最多一个<root>
元素。
<!--
scan 设置为true(默认值) 配置文件变更会重新加载
scanPeriod 检测日志是否修改时间间隔
debug 是否打印logback内部日志
-->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!--
property 自定义定义变量值
name 变量名
value 变量值
scope 作用域
local 作用域在配置文件内有效
context 作用域的有效范围延伸至 logger context
system 作用域的范围最广,整个 JVM 内都有效。
-->
<property scope="context" name="glmapper-name" value="glmapper-demo" />
<!--
也可以引用外部配置文件,配置文件必须为 key-value 型。
-->
<property resource="resource1.properties" />
<!--
contextName logger上下文 用于区分不同应用程序的记录,默认为default
-->
<contextName>${glmapper-name}</contextName>
<!--
appender 负责写日志的组件
-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>%d{HH:mm:ss.SSS} %-5level %logger{80} - %msg%n</Pattern>
</encoder>
</appender>
<!--
用于设置某一个包或某个类日志打印级别以及制定的appender
-->
<logger>
</logger>
<!--
根logger 只有一个level属性,默认为debug
-->
<root level="info">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
配置Logger
使用<logger>
元素配置Logger,Logger配置需注意Logger的级别继承特性。
<logger name="com.ning.test"
level="info" additivity="false">
<appender-ref ref="GLMAPPER-LOGGERONE" />
</logger>
- name:强制性属性,指定Logger名称。可以是某个包或者具体的某个类。
- level:日志打印级别(
TRACE
,DEBUG
,INFO
,WARN
,ERROR
,ALL
和OFF
)。特殊值INHERITED
或者同义词NULL
,代表强制执行父级的级别。如果没有设置此属性,那么当前Logger 将会继承父级的级别。 - addtivity:是否向上级Logger传递打印信息。默认是
true
。 - appender-ref:指定具体的
appender
。可包含0个或多个appender
。
配置根Logger
使用<root>
元素配置根Logger。
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
<root>
只支持单个属性,即日志打印级别属性,可选值有不区分大小写的字符串TRACE,DEBUG,INFO,WARN,ERROR,ALL或OFF吗,默认值为DEBUG。
配置Appender
使用<appender>
元素配置appender。
<!--
appender种类 ConsoleAppender 打印到控制台
FileAppender 打印到文件
RollingFileAppender 上面的子类,滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。
-->
<appender name="NING-LOGGERONE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 文件追加 -->
<append>true</append>
<!--
filter种类 ThresholdFilter 临界值过滤器 过滤掉低于指定临界值的日志
LevelFilter 级别过滤器,根据日志级别进行过滤
-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<file>
./logs/ning-log-logback/ning-loggerone.log
</file>
<!--
rollingPolicy滚动策略 只有appender为RollingFileAppender才有效 涉及文件的移动和重命名
TimeBasedRollingPolicy 时间滚动策略 常用
FixedWindowRollingPolicy 固定窗口算法重命名文件的滚动策略
-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名:按天回滚 daily -->
<FileNamePattern>./logs/ning-log-logback/ning-loggerone.log.%d{yyyy-MM-dd}</FileNamePattern>
<!-- 保留30天 -->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<!--
对记录事件进行格式化 把日志信息转换成字节数组 把字节数组写入到输出流
-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
name:必要属性。指定
appender
名称。class:指定
appender
类实例化的全限定名。- ConsoleAppender:打印到控制台
- FileAppender:打印到文件的
- RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。它是FileAppender的子类
: 为true,日志文件追加。为false,覆写。默认为true。:定义日志文件路径和文件名。 :对日志进行过滤器。可以添加多个filter,按配置顺序过滤。 ThresholdFilter:临界值过滤器,过滤掉低于指定临界值的日志。当日志级别等于或高于临界值时,过滤器返回
NEUTRAL
;当日志级别低于临界值时,日志会被拒绝。LevelFilter:级别过滤器,根据日志级别进行过滤。如果日志级别等于配置级别,过滤器会根据
onMath
(用于配置符合过滤条件的操作) 和onMismatch
(用于配置不符合过滤条件的操作)接收或拒绝日志。<filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter>
:滚动策略,只有当 appender
为RollingFileAppender时,此标签配置有效。涉及文件的移动和重命名。TimeBasedRollingPolicy:最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责触发滚动。下面这段配置表明每天生成一个日志文件,保存30天的日志文件
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--日志文件输出的文件名:按天回滚 daily --> <FileNamePattern> ${logging.path}/glmapper-spring-boot/glmapper-loggerone.log.%d{yyyy-MM-dd} </FileNamePattern> <!--日志文件保留天数--> <MaxHistory>30</MaxHistory> </rollingPolicy>
FixedWindowRollingPolicy:根据固定窗口算法重命名文件的滚动策略。
:对记录事件进行格式化。把日志信息转换成字节数组,再把字节数组写入到输出流。
LogBack运行流程
当用户调用名为com.wombat的Logger的info方法时,Logback内部运行流程如下。
- 获得过滤器链:过滤某些日志打印请求。过滤器返回结果为DENY时,取消打印请求。为NEUTRAL时,进入下一个过滤器。为ACCEPT时,直接跳到步骤3。
- 检查日志级别以决定是否继续打印:logback将Logger的有效级别与请求级别进行比较。
- 创建一个
LoggingEvent
对象:LoggingEvent
对象包含请求的所有相关参数,例如请求的Logger,日志打印级别,日志消息本身,可能与请求一起传递的异常,当前时间,当前线程,有关发出日志记录请求的类的各种数据以及MDC
。 - 调用 Appenders:创建
LoggingEvent
对象后,logback将调用doAppend()
所有适用的appender 的方法,即从logger上下文继承的appender。 - 进行日志信息格式化:被调用的Appender负责格式化日志记录事件。但是,一些(但不是全部)Appender将格式化日志记录事件的任务委托给Layout。Layout会格式化
LoggingEvent
实例,并以字符串形式返回结果。请注意,某些Appender(例如)SocketAppender
不会将日志记录事件转换为字符串,而是将其序列化。因此,它们没有Layout,也不需要Layout。 - 发送
LoggingEvent
到对应的目的地:日志记录事件完全格式化后,每个Appender将其发送到其目的地。