目录
一、初始化配置文件
1、logback 配置文件的初始化顺序
2、logback 内部状态信息
二、配置文件的结构
1、logger 元素
2、root 元素
3、appender 元素
三、配置文件中的变量引用
1、如何定义一个变量
2、为变量设置默认值
3、变量的嵌套
In symbols one observes an advantage in discovery which is greatest when they express the exact nature of a thing briefly and, as it were, picture it; then indeed the labor of thought is wonderfully diminished.
—GOTTFRIED WILHELM LEIBNIZ
大致意思是:使用符号的最大优势,就是符号可以非常清晰、简洁的描绘一个事务的本质,从而可以极大的减少思想劳动的支出。
//我把这段文字贴在这里,因为它阐释了配置文件的本质
Logback 的配置依赖于 Joran 配置框架,这个框架后边如果有时间我会扒一下源码后再进行分享,此处,重点探讨 Logback 配置文件的相关内容。
一、初始化配置文件
1、logback 配置文件的初始化顺序
首先让我们来看一下 logback 尝试加载配置时所遵循的初始化步骤:
(1)查找自定义的 Configurator
Logback 会优先加载自定义的配置器(Configurator),所谓自定义配置器,就是实现了 ch.qos.logback.classic.spi.Configurator 接口的类。
Logback 中提供了两个 Configurator 接口的实现类,一个是 DefaultJoranConfigurator(加载logback.xml文件的配置器),另一个是 BasicConfigurator(默认配置器)。
(2)实例化 SerializedModelConfigurator 配置器
如果在 Logback 没有找到用户提供的自定义配置器,logback 将实例化一个 SerializedModelConfigurator。这是一个序列化模型文件的加载器,加载的文件名称为:"logback-test.scmo" 或 "logback.scmo"。
序列化模型文件的配置执行速度更快,并且不需要任何 XML 库。与 GraalVM 结合使用,可以产生更小的可执行文件,启动速度更快。这个加载器我在 Logback 的核心包中没有找到对应的类,所以暂时不深入分析,了解即可。
如果找不到序列化配置模型文件,SerializedModelConfigurator 将返回一个执行状态,要求调用下一个可用的配置器,即创建并调用 DefaultJoranConfigurator 的实例。
(3)执行 DefaultJoranConfigurator 配置器(重点步骤)
首先,DefaultJoranConfigurator 会尝试查找 “logback.configurationFile” 系统属性上指定的文件。如果可以找到该文件,则会读取并解释该文件,然后进行配置。
如果没有找到上述文件,DefaultJoranConfigurator 将尝试在类路径上查找配置文件 “logback-test.xml” 。如果可以找到该文件,则会读取并解释该文件,然后进行配置。
如果没有找到上述文件,配置器将继续尝试在类路径中查找配置文件 “logback.xml” 。如果可以找到该文件,则会读取并解释该文件,然后进行配置。
如果找不到配置文件,DefaultJoranConfigurator 将返回一个执行状态,要求调用下一个可用的配置器,即创建并调用 BasicConfigurator 的实例。
//从这里可以看出,“logback-test.xml” 文件优先于 “logback.xml” 文件生效。
(4)执行 BasicConfigurator 配置器(使用默认配置)
如果上述配置文件查找均不成功,logback-classic 将使用 BasicConfigurator 进行自身配置,默认配置会使日志定向输出到控制台。执行 BasicConfigurator 配置器,是为了在没有配置文件的情况下,logback 能够正常使用日志记录功能。//默认配置的作用
//从 logback 配置文件加载过程来看,就是使用了一串配置器链(责任链模式)
2、logback 内部状态信息
之前在《如何在项目中快速引入Logback日志?》这篇文章中提到过如何打印 logback 的内部状态信息, 这些信息在对 logback 相关的问题进行诊断时非常有用。文章中,我们使用程序代码的方式对状态信息进行打印,除此之外,还可以使用配置文件的方式。
使用配置文件,本质上就是在配置文件中配置一个状态监听器(StatusListener),如果我们只是想把内部信息打印在控制台上,那么就可以配置一个 OnConsoleStatusListener,配置信息如下:
<configuration>
<!-- 建议: 将状态侦听器置于配置文件的顶部 -->
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
<!-- ... 配置文件的剩余部分 -->
</configuration>
为什么建议将状态侦听器置于配置文件的顶部呢?
这是因为,已注册的状态侦听器将仅接收其注册后的状态事件。它不会接收之前的消息。因此,最好将状态侦听器注册指令放置在配置文件顶部的其他指令之前。
此外,配置 OnConsoleStatusListener 还有一种更简洁的方式,直接在 <configuration> 标签中,将 debug 属性设置为 true 就可以注册 OnConsoleStatusListener,如下所示:
<configuration debug="true">
<!-- ... 配置文件内容部分 -->
</configuration>
二、配置文件的结构
logback 的配置文件非常灵活,且不需要使用 DTD 文件或 XML 模式指定的语法。其配置文件的基本结构可以描述为:<configuration> 元素,包含零个或多个 <appender> 元素,后边可以跟零个或多个 <logger> 元素,最后最多跟随一个 <root> 元素。下图说明了这个基本结构。
logback.xml 配置文件的一般格式内容如下:
<configuration debug="true"> <!--configuration 标签-->
<!--1、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>
<!--2、logger 标签-->
<logger name="self4.Example" level="INFO" additivity="false">
<!--注意:当additivity="true"时可能会重复添加-->
<!--当与root重复添加时,日志会被打印两次,因为有两个一样的appender-->
<appender-ref ref="STDOUT" />
</logger>
<!--3、root 标签-->
<root level="debug">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
1、logger 元素
顾名思义,<logger> 元素就是用来配置 logger 的标签,<logger> 标签只有三个属性,分别为:name(记录器限定名),level(日志级别),additivity(是否追加)。此外,<logger> 元素可以包含零个或多个 <appender-ref> 元素;该元素可以将 appender 添加到指定的记录器中。
其配置示例,可以参考上边 logback.xml 配置文件的一般格式的内容。
使用 <logger> 标签可以分别对不同的 logger 设置不同的 level 级别,如果不设置 <logger> 标签,那么所有的 logger 都会继承根 logger 的配置,也就是 <root> 标签的配置。
有关 logger 继承的详细内容,我在这篇文章《Logback 日志框架的架构》中有介绍,可做参考。
2、root 元素
<root> 元素用来配置根记录器(root logger),该标签只支持单一属性的 level 属性。
因为根记录器是最顶层的记录器,所以它没有可追加属性(additivity),同时,根记录器已经被命名为 “ROOT”,因此它也不允许使用名称属性(name)。此外,<root> 元素可以包含零个或多个 <appender-ref> 元素;该元素可以将 appender 添加到根记录器中。
3、appender 元素
<appender> 元素用来配置 appender,该标签有两个强制属性 name 和 class。所谓强制就是必须要配置的意思,其中 name 用来指定 appender 的名称,class 用来指定要实例化的 appender 类的完全限定名称。
<appender> 元素可以包含零个或一个 <layout> 元素、零个或多个 <encoder> 元素以及零个或多个 <filter> 元素。除了这三个公共元素之外,<appender> 元素还可以包含与 appender 类的 JavaBean 属性相对应的任意数量的元素。//也就是说Appender实例中的一些属性也可以在配置文件中进行配置
下图说明了<appender> 元素常见的结构。
<layout> 元素采用强制的 class 属性,要求必须指定要实例化的布局类的完全限定名称。与 <appender> 元素一样,<layout> 可以包含与布局实例的属性相对应的其他元素。由于 <layout> 元素经常被配置,所以如果布局类是 PatternLayout,则可以根据默认的类映射规则省略 class 属性。//指定字符串的打印格式
<encoder> 元素也采用强制的 class 属性,同 <layout> 元素一样,它也具有默认的映射,如果编码器类是 PatternLayoutEncoder,则可以按照默认的类映射规则省略 class 属性。
简要的示例配置内容如下所示:
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>myApp.log</file>
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] -%kvp- %msg%n</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%kvp %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
<appender-ref ref="STDOUT" />
</root>
</configuration>
三、配置文件中的变量引用
与许多脚本语言一样,logback 配置文件也支持对变量进行定义和引用。变量可以定义在配置文件的内部,也可以定义在配置文件的外部(不推荐),甚至还可以动态的进行计算和定义。
引用一个变量,可以使用 "${name}" 这种格式,"${name}" 会被解释为对 name 的属性值的引用。
1、如何定义一个变量
由于历史原因,在配置文件中定义一个变量,logback 1.0.7 版本之前使用 <property> 标签,logback 1.0.7 及以后的版本中可以使用 <variable> 标签,当然也可以使用 <property> 标签(兼容性)。
在配置文件中定义变量的内容如下:
<configuration>
<!--定义一个变量,变量名称为USER_HOME -->
<variable name="USER_HOME" value="/home/sebastien" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<!--引用定义的变量-->
<file>${USER_HOME}/myApp.log</file>
<encoder>
<pattern>%kvp %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>
如果想将变量定义在外部文件中,可以使用如下配置:
<configuration>
<!--引入外部文件-->
<variable file="src/main/java/chapters/configuration/variables1.properties" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${USER_HOME}/myApp.log</file> <!--引入外部文件中的USER_HOME变量值-->
<encoder>
<pattern>%kvp %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>
外部文件 variables1.properties 中的内容如下://键值对
USER_HOME=/home/sebastien
2、为变量设置默认值
在某些情况下,如果变量未被声明或者它的值为 null 时,则可能希望变量具有默认值。在 logback 配置中,可以使用 " :- " 运算符来指定默认值,比如,名为 name 的变量没有被定义,那么 "{name:-golden}" 将被解释为 "golden"。
3、变量的嵌套
logback 的配置完全支持变量的嵌套。变量的名称、默认值和值的定义都可以引用其他变量。
变量值的定义可以包含对其他变量的引用,比如:
#定义的变量
USER_HOME=/home/sebastien
fileName=myApp.log
#该变量值的定义可以引用其他变量
destination=${USER_HOME}/${fileName}
变量名称的定义也可以包含对其他变量的引用,比如,如果为名为 "userid" 的变量的值为:"swadian",则 "${${userid}.password}" 引用是变量名为 "swadian.password" 的值。
变量默认值的定义同样也可以包含对其他变量的引用,例如,如果变量 "id" 未被分配值,并且变量 "userid" 的值为 "swadian",则表达式 "${id:-${userid}}" 将返回 "swadian"。
当然,logback 配置中还有许多值得探讨的内容,但因为时间和篇幅有限,我们只探讨了一些常用的配置项,其他详细内容可以点击此处查阅官方文档。
至此,全文结束。