阅读logback文档

This commit is contained in:
asahi
2024-08-05 20:55:25 +08:00
parent 080746df22
commit 69f07de1f6

View File

@@ -189,3 +189,326 @@ logback支持对配置文件的变动进行扫描并且对扫描到的变动
</configuration>
```
#### `Appender`
`<appender>`元素用于配置Appender其接收两个必填属性`name``class`。其中,`name`属性指定了appender的名称`class`属性则是指定了appender Class的全类名。
`appender`元素可以包含0或多个`<layout>`元素0或多个`<encoder>`元素0或多个`<filter>`元素。除了可以包含上述公共元素外,还`appender`元素还可以包含任意数量javaBean属性相关的元素
#### `Layout`
`<layout>`元素接收一个必填`class`属性值为Layout类的全类名。和appender元素一样layout也可以包含javaBean属性相关的元素。如果layout class为`PatternLayout`那么class属性可以省略。
#### `encoder`
`<encoder>`元素接收一个必填的class属性值为Encoder的全类名。如果encoder类为`PatternLayoutEncoder`那么class属性可以省略。
当想要向多个appender中输出日志时只需要在logger中定义多个appender即可示例如下
```xml
<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>
```
上述配置中定义了两个appender`FILE``STDOUT``FILE` appender将日志打印到`myApp.log`文件中。`STDOUT` appender则是将日志打印到控制台。
root logger通过`<appender-ref>`标签来引用appender每个appender都有其自己的encoderencoder通常在appender之间并不共享。同样的layout也不会在多个appender之间共享。
> 由于logger不仅会将日志发送到其自身的logger并且还会将日志发送给其先祖logger的appender故而将相同的appender共享给拥有上下级关系的logger将会导致日志的重复打印。
可以通过设置`additivity`属性来设置后代logger不继承先祖logger的appender配置示例如下所示
```xml
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>foo.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>%msg%n</pattern>
</encoder>
</appender>
<logger name="chapters.configuration.Foo" additivity="false">
<appender-ref ref="FILE" />
</logger>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
```
### 变量替换
可以以`${VAR_NAME}`的形式来使用变量替换,且`HOSTNAME``CONTEXT_NAME`变量是默认定义的。
如下示例展示了如何定义变量:
```xml
<configuration>
<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>
```
变量除了可以在配置文件中定义还可以通过System Properties进行传递
```shell
java -DUSER_HOME="/home/sebastien" MyApp2
```
```xml
<configuration>
<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>
```
当存在多个变量时,可以单独为变量定义一个文件:
```xml
<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>
<encoder>
<pattern>%kvp %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>
```
```properties
USER_HOME=/home/sebastien
```
除了通过`<variable>`元素的file属性指定变量文件位置外还可以通过`variable`元素的resource属性指定classpath下的文件路径
```xml
<configuration>
<variable resource="resource1.properties" />
<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>
```
#### 变量作用域
一个变量可以被定义在如下作用域:
- LOCAL SCOPE: 具有local作用域的变量生命周期为从其文件定义该变量的位置开始一直到该配置文件的末尾
- CONTEXT SCOPE: 具有context作用域的变量在context存在期间一直存在直到context执行clear操作
- SYSTEM SCOPE: 具有system作用域的变量将会添加到jvm的system properties中生命周期和jvm相同直到该变量被清空
在变量替换时变量首先从local scope中查找其次到context scope中找再其次到system scope中找最后到os环境变量中找。
`<variable>`元素中可以含有`scope`属性,其值可以为`local`,`context``system`。如果没有指定scope属性默认是`local`
```xml
<configuration>
<variable scope="context" name="nodeId" value="firstNode" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>/opt/${nodeId}/myApp.log</file>
<encoder>
<pattern>%kvp %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>
```
在上述示例中,尽管`nodeId`变量被定义为context scope其在每个logging event中都可以使用。
#### 为变量赋值默认值
可以按`"${aName:-golden}"`的方式为指定返回的默认值当aName变量未定义时返回值为`golden`
#### 变量嵌套
在定义变量时,支持变量的嵌套:
```properties
USER_HOME=/home/sebastien
fileName=myApp.log
destination=${USER_HOME}/${fileName}
```
并且,表达式也支持嵌套变量:
`"${${userid}.password}"`表达式在`userid`变量为`asahi`的情况下,表示`asahi.password`变量的值
在为变量指定默认值时,默认值也可以通过变量来指定,例如`${id:-${userid}}`,在`id`变量未定义时,会返回`userid`变量的值。
### 条件处理配置文件
logback配置文件支持通过`<if>`,`<then>`,`<else>`元素来条件指定配置。条件处理需要`janino`库。
条件处理语法如下:
```xml
<!-- if-then form -->
<if condition="some conditional expression">
<then>
...
</then>
</if>
<!-- if-then-else form -->
<if condition="some conditional expression">
<then>
...
</then>
<else>
...
</else>
</if>
```
条件为java表达式且只允许访问system properties和context properties。`proerpty()`方法或`p()`方法将会返回变量的值,例如`p('k')``property('k')`将会返回变量`k`的值。如果`k`变量未定义,将会返回空字符串。
`isDefined`方法则是可以用来检查指定变量是否被定义。并且,可以通过`isNull`方法来检查指定变量的值是否为空。
```xml
<configuration debug="true">
<if condition='property("HOSTNAME").contains("torino")'>
<then>
<appender name="CON" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %-5level %logger{35} -%kvp- %msg %n</pattern>
</encoder>
</appender>
<root>
<appender-ref ref="CON" />
</root>
</then>
</if>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${randomOutputDir}/conditional.log</file>
<encoder>
<pattern>%d %-5level %logger{35} -%kvp- %msg %n</pattern>
</encoder>
</appender>
<root level="ERROR">
<appender-ref ref="FILE" />
</root>
</configuration>
```
## Appender
logback把日志事件的写操作委托给了Appender组件。Appender必须实现`ch.qos.logback.core.Appender`接口,接口主要方法如下:
```java
package ch.qos.logback.core;
import ch.qos.logback.core.spi.ContextAware;
import ch.qos.logback.core.spi.FilterAttachable;
import ch.qos.logback.core.spi.LifeCycle;
public interface Appender<E> extends LifeCycle, ContextAware, FilterAttachable {
public String getName();
public void setName(String name);
void doAppend(E event);
}
```
其中doAppend方法负责将日志输出到对应的输出设备。
appender是命名实体故而其可以通过name来进行引用。Appender实现了FilterAttachable接口故而可以将一个或多个filter关联到appender。
appender负责将日志事件打印到输出设备但是appender可以将日志的格式化操作委托给`Layout``Encoder`对象。每个layout或encoder都只会关联一个appender一些logger拥有内置的固定format格式对于用于内置固定format的appender其不需要encoder或layout。
> 例如SocketAppender并没有encoder或layout其只是将日志事件序列化并且通过网络传输序列化后的数据
### AppenderBase
`ch.qos.logback.core.AppenderBase`是一个实现了Appender接口的抽象类其针对appender中的部分接口提供了实现。logback中内置的所有apender实现都实现了该抽象类。
`AppenderBase`类实现了doAppend方法并留下了`append`方法供实现类进行实现。AppenderBase类中doAppend方法被synchronized修饰多线程环境下doAppend方法的调用是阻塞的。
### Logback Core
logback core是其他logback模块的基础。其内置了如下开箱即用的Appender。
#### OutputStreamAppender
`OutputStreamAppender`会将日志事件追加到`java.io.OutputStream`中,该类为其他基于`OutputSteamAppender`的appender提供了基础。
ConsoleAppender和FileAppender都继承了OutputStreamAppender。
#### ConsoleAppender
ConsoleAppender会将日志追加到console`System.out``System.err`中,前者是默认的目标设备。`ConsoleAppender`通过用户指定的`encoder`来对日志事件进行格式化。
ConsoleAppender拥有如下property
- encoder : `Encoder`类型
- target : 字符串类型,值为`System.out``System.err`
- withJansi : boolean类型默认为false、
如下为ConsoleAppender的使用示例
```xml
<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>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg %n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
```
#### FileAppender
`FileAppender``OutputStreamAppender`的子类,将日志事件打印到文件中。目标文件通过`File`选项来指定,如果指定文件已经存在,是否清空文件内容或将日志追加到文件末尾,取决于`append`属性。
FileAppender拥有如下property
- append: boolean类型当文件已经存在时如果设置为true日志追加到文件末尾如果日志设置为false已存在文件的内容将会被清空。默认该属性被设置为true
- encoderEncoder类型
- file字符串类型为写入文件的文件名如果文件不存在那么文件会被创建。如果该路径值中存在不存在的目录FileAppender会自动创建目录。
- bufferSizeFileSize类型当immediateFlush属性被设置为false时bufferSize选项将会设置output buffer的大小