From 69f07de1f6fdc73a0e3eec94b8d7c902cde6b661 Mon Sep 17 00:00:00 2001 From: asahi Date: Mon, 5 Aug 2024 20:55:25 +0800 Subject: [PATCH] =?UTF-8?q?=E9=98=85=E8=AF=BBlogback=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring/logback/logback.md | 323 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 323 insertions(+) diff --git a/spring/logback/logback.md b/spring/logback/logback.md index a567fac..887346f 100644 --- a/spring/logback/logback.md +++ b/spring/logback/logback.md @@ -189,3 +189,326 @@ logback支持对配置文件的变动进行扫描,并且对扫描到的变动 ``` + +#### `Appender` +``元素用于配置Appender,其接收两个必填属性:`name`和`class`。其中,`name`属性指定了appender的名称,而`class`属性则是指定了appender Class的全类名。 + +`appender`元素可以包含0或多个``元素,0或多个``元素,0或多个``元素。除了可以包含上述公共元素外,还`appender`元素还可以包含任意数量javaBean属性相关的元素, + +#### `Layout` +``元素接收一个必填`class`属性,值为Layout类的全类名。和appender元素一样,layout也可以包含javaBean属性相关的元素。如果layout class为`PatternLayout`,那么class属性可以省略。 + +#### `encoder` +``元素接收一个必填的class属性,值为Encoder的全类名。如果encoder类为`PatternLayoutEncoder`,那么class属性可以省略。 + +当想要向多个appender中输出日志时,只需要在logger中定义多个appender即可,示例如下 + +```xml + + + + myApp.log + + + %date %level [%thread] %logger{10} [%file:%line] -%kvp- %msg%n + + + + + + %kvp %msg%n + + + + + + + + +``` +上述配置中,定义了两个appender,`FILE`和`STDOUT`。 `FILE` appender将日志打印到`myApp.log`文件中。`STDOUT` appender则是将日志打印到控制台。 + +root logger通过``标签来引用appender,每个appender都有其自己的encoder,encoder通常在appender之间并不共享。同样的,layout也不会在多个appender之间共享。 + +> 由于logger不仅会将日志发送到其自身的logger,并且还会将日志发送给其先祖logger的appender,故而将相同的appender共享给拥有上下级关系的logger将会导致日志的重复打印。 + +可以通过设置`additivity`属性来设置后代logger不继承先祖logger的appender,配置示例如下所示: +```xml + + + + foo.log + + %date %level [%thread] %logger{10} [%file : %line] -%kvp- %msg%n + + + + + + %msg%n + + + + + + + + + + + +``` + +### 变量替换 +可以以`${VAR_NAME}`的形式来使用变量替换,且`HOSTNAME`和`CONTEXT_NAME`变量是默认定义的。 + +如下示例展示了如何定义变量: +```xml + + + + + + ${USER_HOME}/myApp.log + + %kvp %msg%n + + + + + + + +``` + +变量除了可以在配置文件中定义,还可以通过System Properties进行传递, +```shell +java -DUSER_HOME="/home/sebastien" MyApp2 +``` +```xml + + + + ${USER_HOME}/myApp.log + + %kvp %msg%n + + + + + + + +``` +当存在多个变量时,可以单独为变量定义一个文件: +```xml + + + + + + ${USER_HOME}/myApp.log + + %kvp %msg%n + + + + + + + +``` +```properties +USER_HOME=/home/sebastien +``` +除了通过``元素的file属性指定变量文件位置外,还可以通过`variable`元素的resource属性指定classpath下的文件路径: +```xml + + + + + + ${USER_HOME}/myApp.log + + %kvp %msg%n + + + + + + + +``` + +#### 变量作用域 +一个变量可以被定义在如下作用域: +- LOCAL SCOPE: 具有local作用域的变量,生命周期为从其文件定义该变量的位置开始,一直到该配置文件的末尾 +- CONTEXT SCOPE: 具有context作用域的变量,在context存在期间一直存在,直到context执行clear操作 +- SYSTEM SCOPE: 具有system作用域的变量,将会添加到jvm的system properties中,生命周期和jvm相同,直到该变量被清空 + +在变量替换时,变量首先从local scope中查找,其次到context scope中找,再其次到system scope中找,最后到os环境变量中找。 + +``元素中可以含有`scope`属性,其值可以为`local`,`context`或`system`。如果没有指定scope属性,默认是`local`。 + +```xml + + + + + + /opt/${nodeId}/myApp.log + + %kvp %msg%n + + + + + + + +``` +在上述示例中,尽管`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配置文件支持通过``,``,``元素来条件指定配置。条件处理需要`janino`库。 + +条件处理语法如下: +```xml + + + + ... + + + + + + + ... + + + ... + + +``` + +条件为java表达式,且只允许访问system properties和context properties。`proerpty()`方法或`p()`方法将会返回变量的值,例如`p('k')`或`property('k')`将会返回变量`k`的值。如果`k`变量未定义,将会返回空字符串。 + +`isDefined`方法则是可以用来检查指定变量是否被定义。并且,可以通过`isNull`方法来检查指定变量的值是否为空。 + +```xml + + + + + + + %d %-5level %logger{35} -%kvp- %msg %n + + + + + + + + + + ${randomOutputDir}/conditional.log + + %d %-5level %logger{35} -%kvp- %msg %n + + + + + + + +``` +## 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 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 + + + + + + %-4relative [%thread] %-5level %logger{35} -%kvp- %msg %n + + + + + + + +``` +#### FileAppender +`FileAppender`是`OutputStreamAppender`的子类,将日志事件打印到文件中。目标文件通过`File`选项来指定,如果指定文件已经存在,是否清空文件内容或将日志追加到文件末尾,取决于`append`属性。 + +FileAppender拥有如下property: +- append: boolean类型,当文件已经存在时,如果设置为true,日志追加到文件末尾,如果日志设置为false,已存在文件的内容将会被清空。默认该属性被设置为true +- encoder:Encoder类型 +- file:字符串类型,为写入文件的文件名,如果文件不存在,那么文件会被创建。如果该路径值中存在不存在的目录,FileAppender会自动创建目录。 +- bufferSize:FileSize类型,当immediateFlush属性被设置为false时,bufferSize选项将会设置output buffer的大小