Appender
我们在基础配置中简单介绍了appender的配置以及用法,那么这篇我们就来进阶了解一下appender!
回顾一下:什么是Appender
我们在上一篇基本配置的介绍中,我们知道logback 将写入日志事件的任务委托给一个名为 appender 的组件,所以appender是负责日志的输出或者打印事宜。以及大致有一个印象,日志的打印和一个叫doAppend()
的方法有关。这篇我们就具体谈谈这些。
源码出发
appender起源在哪里呢?我们通过官方的文档可以知道最顶级的接口Appender
是这一切的开端。
public interface Appender<E> extends LifeCycle, ContextAware, FilterAttachable<E> {
/**
* Get the name of this appender. The name uniquely identifies the appender.
*/
String getName();
/**
* This is where an appender accomplishes its work. Note that the argument
* is of type Object.
* @param event
*/
void doAppend(E event) throws LogbackException;
/**
* Set the name of this appender. The name is used by other components to
* identify this appender.
*
*/
void setName(String name);
}
doAppender()
方法接收一个泛型参数 E 作为唯一的参数。E 的实际参数类型取决于 logback 模块。在 logback-classic 模块里面,E 的类型是 ILoggingEvent
。在 logback-access 模块里面,E 的类型是 AccessEvent
。doAppend()
是 logback 框架里面最重要的模块。它的责任是将日志事件进行格式化,然后输出到对应的设备上。我们以logback为例打开源码LoggingEvent
这个类,这些属性就是E的内容,我们这里大致只要先知道里面存的是日志内容相关的信息。
两个顶级抽象类:AppenderBase
,UnsynchronizedAppenderBase
,在官方文档中对AppenderBase
的介绍有一句话
It is the super-class of all appenders shipped with logback.
这个不够全面,应该要包含UnsynchronizedAppenderBase
,因为这两个加起来才是logback中所有的具体的appender的父类。
因为AppenderBase
的实现是 synchronized 的。不同的线程通过同一个 appender 打印日志是线程安全的。当一个线程 T 正在执行 doAppend()
方法,接下来其它的线程调用将会被阻塞直到线程 T 离开 doAppend()
方法,这样可以确保 T 对 appender 的访问具有独占性。这种日志打印的方式其实对我们线上项目是不够友好的,很多时候我们需要的是多线程的打印方式,UnsynchronizedAppenderBase
能够很好满足我们的需求,所以我们的介绍也会以实现UnsynchronizedAppenderBase
的appender为介绍对象。
OutputStreamAppender
OutputStreamAppender
将事件附加到 java.io.OutputStream
上。这个类提供了其它 appender 构建的基础服务。用户通常不会直接实例一个 OutputStreamAppender 实例。因为一般来说 java.io.OutputStream
类型不能方便的转为 String。因为在配置文件中没有方法去直接指定一个 OutputStream
目标对象。简单来说,你不能通过配置文件配置一个 OutputStreamAppender
。但是这并不意味着 OutputStreamAppender
缺少配置属性。
属性名 | 属性值 | 描述 |
---|---|---|
ecoder | Encoder | 决定通过哪种方式将事件写入 OutputStreamAppender ,Encoder 将会在单独的章节介绍 |
immediateFlush | boolean | immediateFlush 的默认值为 true。立即刷新输出流可以确保日志事件被立即写入,并且可以保证一旦你的应用没有正确关闭 appender,日志事件也不会丢失。从另一方面来说,设置这个属性为 false,有可能会使日志的吞吐量翻两番(视情况而定)。但是,设置为 false,当应用退出的时候没有正确关闭 appender,会导致日志事件没有被写入磁盘,可能会丢失。 |
这个appender并不是我们需要具体了解的,我们主要了解他的子类。
OutputStreamAppender
是其他三个 appender 的父类,分别是 ConsoleAppender
、FileAppender
以及 RollingFileAppender
。FileAppender
又是 RollingFileAppender
的父类。这三个才是我们真正要具体掌握的!
ConsoleAppender
ConsoleAppender
将日志事件附加到控制台,更具体来说其实是通过 System.out
或者 System.err
来进行输出。默认通过前者。ConsoleAppender
通过用户指定的 encoder,格式化日志事件。Encoder 会在接下来的章节讨论。System.out
与System.err
两者都是 java.io.PrintStream
类型。因此,它们被包装在可以进行 I/O 缓存操作的 OutputStreamWriter
中。
属性名 | 类型 | 描述 |
---|---|---|
encoder | Encoder | 见 OutputStreamAppender 属性 |
target | String | System.out 或 System.err。默认为 System.out |
withJansi | boolean | withJansi 的默认值为 false 。设置 withJansi 为 true 可以使用 ANSI 彩色代码。在 windows 上如果设置为 true,你应该将 org.fusesource.jansi:jansi:1.9 这个 jar 包放到 classpath 下。基于 Unix 实现的操作系统,像 Linux、Max OS X 都默认支持 ANSI 才彩色代码。 |
示例配置:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender" >
<!-- encoder 默认使用 ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
<!--<target>System.err</target>-->
<!--<withJansi>true</withJansi>-->
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
FileAppender
FileAppender
是 OutputStreamAppender
的子类,将日志事件输出到文件中。通过 file 来指定目标文件。如果该文件存在,根据 append
的值,要么将日志追加到文件中,要么该文件被截断。
属性名 | 类型 | 描述 |
---|---|---|
append | boolean | 如果为 true ,日志事件会被追加到文件中,否则的话,文件会被截断。默认为 true |
encoder | Encoder | 参见 OutputStreamAppender 的属性 |
file | String | 要写入文件的名称。如果文件不存在,则新建。在 windows 平台上,用户经常忘记对反斜杠进行转义。例如,c:temptest.log 不会被正确解析,因为 't' 是一个转义字符,会被解析为一个 tab 字符 (u0009)。正确的值应该像:c:/temp/test.log 或者 c:\temp\test.log。没有默认值。 |
prudent | boolean | 如果是 true,日志会被安全的写入文件,即使其他的FileAppender 也在向此文件做写入操作,效率。默认是 false。 |
例如
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>testFile.log</file>
<!-- 将 immediateFlush 设置为 false 可以获得更高的日志吞吐量 -->
<immediateFlush>true</immediateFlush>
<!-- 默认为 ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
如上配置会在当前项目目录下生成一个testFile.log
文件,内容为:
250 [main] INFO com.exercise.appenderconfig.MyApp - hello,now is 2020-01-08T20:07:16.236
RollingFileAppender
RollingFileAppender
继承自FileAppender
,具有轮转日志文件的功能。白话文来说:RollingFileAppender
将日志输出到 log.txt 文件,在满足了特定的条件之后,比如第二天把前一天的日志打包起来,或者将日志放到另一个地方。
与 RollingFileAppender
进行交互的有两个重要的子组件。第一个是 RollingPolicy
,轮转策略,它负责日志轮转的功能,即轮转日志做什么事情;另一个是 TriggeringPolicy
,触发策略,它负责日志轮转的时机,即什么条件下进行轮转。
为了让 RollingFileAppender
生效,必须同时设置 RollingPolicy
与 TriggeringPolicy
。但是,如果 RollingPolicy
也实现了TriggeringPolicy
接口,那么只需要设置前一个就好了。
RollingFileAppender
的属性如下所示:
属性名 | 类型 | 描述 |
---|---|---|
file | String | 参见 FileAppender |
append | boolean | 参见 FileAppender |
encoder | Encoder | 参见 OutputStreamAppender |
rollingPolicy | RollingPolicy | 当轮转发生时,指定 RollingFileAppender 的行为 |
triggeringPolicy | TriggeringPolicy | 告诉 RollingFileAppender 什么时候发生轮转行为 |
prudent | boolean | FixedWindowRollingPolicy 不支持该属性。 其它的参考 FileAppender |
Rolling policy 简介
RollingPolicy
负责轮转的方式为:移动文件以及对文件改名。
接口源码如下:
package ch.qos.logback.core.rolling;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.spi.LifeCycle;
public interface RollingPolicy extends LifeCycle {
public void rollover() throws RolloverFailure;
public String getActiveFileName();
public CompressionMode getCompressionMode();
public void setParent(FileAppender appender);
}
rollover
方法负责对日志文件进行归档。getActiveFileName()
方法负责获取当前日志文件的名字。getCompressionMode
方法决定采取哪种压缩模式。通过 setParent
方法引用父类。
常用的轮转模式:TimeBasedRollingPolicy
它是基于时间来定义轮转策略。例如按天或者按月。TimeBasedRollingPolicy
既负责轮转的行为,也负责触发轮转。实际上,TimeBasedRollingPolicy
同时实现了 RollingPolicy
与 TriggeringPolicy
接口。
TimeBasedRollingPolicy
的配置中 fileNamePattern
是必选项,其它的属性可选。
属性名 | 类型 | 描述 |
---|---|---|
fileNamePattern | String | 该属性定义了轮转时的属性名。它的值应该由文件名加上一个 %d 的占位符。%d 应该包含 java.text.SimpleDateFormat 中规定的日期格式。如果省略日期格式,默认为 yyyy-MM-dd。我们一般会在RollingFileAppender 指定当前日志的文件名,触发轮转后将之前的日志进行转储。注意:斜杆 '/' 或者反斜杠 '' 都会被解析成目录分隔符。 可以指定多个 %d,但是只能有一个是主要的,用于推断轮转周期。其它的 %d 占位符必须通过 'aux' 标记为辅助的。 |
maxHistory | int | 这个可选的属性用来控制最多保留多少数量的归档文件,将会异步删除旧的文件。比如,你指定按月轮转,指定 maxHistory = 6,那么 6 个月内的归档文件将会保留在文件夹内,大于 6 个月的将会被删除。注意:当旧的归档文件被移除时,当初用来保存这些日志归档文件的文件夹也会在适当的时候被移除。 |
totalSizeCap | int | 这个可选属性用来控制所有归档文件总的大小。当达到这个大小后,旧的归档文件将会被异步的删除。使用这个属性时还需要设置 maxHistory 属性。maxHistory 优先级会高于totalSizeCap。 |
cleanHistoryOnStart | boolean | 如果设置为 true,那么在 appender 启动的时候,归档文件将会被删除。默认的值为 false。 归档文件的删除通常在轮转期间执行。但是,有些应用的存活时间可能等不到轮转触发。对于这种短期应用,可以通过设置该属性为 true,在 appender 启动的时候执行删除操作。 |
该模式的轮转周期是通过 fileNamePattern 推断出来的。在某些情况下,你可能想要根据时区而不是主机的时钟来轮转日志,具体见官方文档,这里不做叙述。
TimeBasedRollingPolicy
支持文件自动压缩。如果 fileNamePattern
以 .gz 或者 .zip 结尾,将会启动这个特性。
下面是关于 fileNamePattern
的一些常见示例:
fileNamePattern | 轮转周期 | 示例 |
---|---|---|
/wombat/foo.%d | 每天轮转(晚上零点)。日期格式默认为 yyyy-MM-dd | 没有设置 file 属性:在 2006.11.23 这一天的日志都会输出到 /wombat/foo.2006-11-23 这个文件。晚上零点以后,日志将会输出到 wombat/foo.2016-11-24 这个文件。 设置 file 的值为 /wombat/foo.txt:在 2016.11.23 这一天的日志将会输出到 /wombat/foo.txt 这个文件。在晚上零点的时候,foo.txt 将会被改名为 /wombat/foo.2016-11-23。然后将创建一个新的 foo.txt,11.24 号这一天的日志将会输出到这个新的文件中。 |
/wombat/%d{yyyy/MM}/foo.txt | 每个月开始的时候轮转 | 没有设置 file 属性:在 2016.10 这一个月中的日志将会输出到 /wombat/2006/10/foo.txt。在 10.31 晚上凌晨以后,11 月份的日志将会被输出到 /wombat/2006/11/foo.txt。 设置 file 的值为 /wombat/foo.txt:在 2016.10,这个月份的日志都会输出到 /wombat/foo.txt。在 10.31 晚上零点的时候,/wombat/foo.txt 将会被重命名为 /wombat/2006/10/foo.txt,并会创建一个新的文件 /wombat/foo.txt ,11 月份的日志将会输出到这个文件。依此类推。 |
/wombat/foo.%d{yyyy-ww}.log | 每周的第一天(取决于时区) | 每次轮转发生在每周的第一天,其它的跟上一个例子类似 |
/wombat/foo%d{yyyy-MM-dd_HH}.log | 每小时轮转 | 跟之前的例子类似 |
/wombat/foo%d{yyyy-MM-dd_HH-mm}.log | 每分钟轮转 | 跟之前的例子类似 |
/wombat/foo%d{yyyy-MM-dd_HH-mm, UTC}.log | 每分钟轮转 | 跟之前的例子类似,不过时间格式是 UTC |
/foo/%d{yyyy-MM, aux}/%d.log | 每天轮转。归档文件在包含年月的文件夹下 | 第一个 %d 被辅助标记。第二个 %d 为主要标记,但是日期格式省略了。因此,轮转周期为每天(由第二个 %d 控制),文件夹的名字依赖年与月。例如,在 2016.11 的时候,所有的归档文件都会在 /foo/2006-11/ 文件夹下,如:/foo/2006-11/2006-11-14.log |
/wombat/foo.%d.gz | 每天轮转(晚上零点),自动将归档文件压缩成 GZIP 格式 | file 属性没有设置:在 2009.11.23,日志将会被输出到 /wombat/foo.2009-11-23 这个文件。但是,在晚上零点的时候,文件将会被压缩成 /wombat/foo.2009-11-23.gz。在 11.24,这一天的日志将会被直接输出到 /wombat/folder/foo.2009-11-24 这个文件。file 属性的值设置为 /wombat/foo.txt:在 2009.11.23,日志将会被输出到 /wombat/foo.txt 这个文件。在晚上零点的时候,该文件会被压缩成 /wombat/foo.2009-11-23.gz。并会创建一个新的 /wombat/foo.txt 文件,11.24 这一天的日志将会被输出到该文件。依此类推。 |
注意:轮转并不是时间驱动的,而是依赖日志事件。例如,在 2020.01.08,假设 fileNamePattern
的值为 yyyy-MM-dd(按天轮转),在晚上零点之后,没有日志事件到来,假设在 23 分 47 秒之后,第一个到达的日志事件将会触发轮转。也就是说轮转实际发生在 01.09 00:23'47 AM 而不是 0:00 AM。因此,依赖日志事件的到达速度,所以轮转可能会有延迟。但是!不管延迟的情况是什么样,一定周期内生成的日志事件将会被输出到指定的文件中。
示例:
<configuration>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logFile.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>logFile.%d{yyyy-MM-dd}.gz</FileNamePattern>
<MaxHistory>180</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
基于大小以及时间的轮转策略:SizeAndTimeBasedRollingPolicy
有时你希望按时轮转,但同时又想限制每个日志文件的大小。特别是如果后期处理工具需要对日志进行大小限制。为了满足这个需求,logback 配备了 SizeAndTimeBasedRollingPolicy
。
TimeBasedRollingPolicy
是所有的限制归档文件总大小。而SizeAndTimeBasedRollingPolicy
是限制每个日子文件的大小。
例如:
<configuration debug="true">
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>mylog.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 按天轮转 -->
<fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="ROLLING" />
</root>
</configuration>
SizeAndTimeBasedRollingPolicy
的fileNamePattern
属性除了%d
之外%i
也是必要属性。在当前时间还没有到达周期轮转之前,日志文件达到了 maxFileSize
指定的大小,会进行归档,%i
是轮转的索引,递增索引从 0 开始。如:mylog-2020-01-08.0.txt
,mylog-2020-01-08.1.txt
。
当然默认的轮转算法不止这些还有诸如:FixedWindowRollingPolicy
(根据固定窗口算法重命名文件的轮转策略),SizeBasedTriggeringPolicy
(观察当前活动文件的大小,如果已经大于了指定的值,它会给 RollingFileAppender
发一个信号触发对当前活动文件的轮转)的触发策略
如果这些常用的还不能满足你的需求也可以自定义appender~
Encoder
什么是Encoder
Encoder 将日志事件转换为字节数组,同时将字节数组写入到一个 OutputStream
中。Encoder 在 logback 0.9.19 版本引进,旨在代替layouts。现在的layouts都是通过LayoutWrappingEncoder
来实现 encoder 与 layout 进行交互,它实现了 encoder 接口并且包裹了一个 layout,通过委托该 layout 将日志事件转换为字符串。
PatternLayoutEncoder
是目前真正唯一有用的 encoder。它仅仅包裹了一个 PatternLayout
就完成了大部分的工作。
源码出发
package ch.qos.logback.core.encoder;
public interface Encoder<E> extends ContextAware, LifeCycle {
/**
* Get header bytes. This method is typically called upon opening of
* an output stream.
*
* @return header bytes. Null values are allowed.
*/
byte[] headerBytes();
/**
* Encode an event as bytes.
*
* @param event
*/
byte[] encode(E event);
/**
* Get footer bytes. This method is typically called prior to the closing
* of the stream where events are written.
*
* @return footer bytes. Null values are allowed.
*/
byte[] footerBytes();
}
我们可以看到这个接口其实很简单,但是可以做的事却远不只看到的如此!
PatternLayoutEncoder
由于 PatternLayout
是最常用的 layout,logback 使用 PatternLayoutEncoder
来满足这种用法。它扩展了 LayoutWrappingEncoder
,被限制用来包裹 PatternLayout
实例。
在 logback 0.9.19 版本,无论 FileAppender
还是其子类通过 PatternLayout
来进行配置,都必须使用 PatternLayoutEncoder
来代替。
immediateFlush 属性
在 LOGBACK 1.2.0
中, immediateFlush
属性是 appender 的一部分。
用格式化字符串作为开头
为了帮助解析日志文件,logback 可以将格式化字符串插入到日志文件的顶部。这个功能默认是关闭的。可以为相关的 PatternLayoutEncoder
设置 outputPatternAsHeader
属性的值为 true
来开启这个功能。下面是示例:
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>foo.log</file>
<encoder>
<pattern>%d %-5level [%thread] %logger{0}: %msg%n</pattern>
<outputPatternAsHeader>true</outputPatternAsHeader>
</encoder>
</appender>
将会在日志文件中输出类似下面的日志:
#logback.classic pattern: %d [%thread] %-5level %logger{36} - %msg%n
2012-04-26 14:54:38,461 [main] DEBUG com.foo.App - Hello world
2012-04-26 14:54:38,461 [main] DEBUG com.foo.App - Hi again
...
以 "#logback.classic pattern" 开头的行就是新插入的行。
格式化语法:pattern
每一个转换说明符由一个百分号开始 '%',后面跟随可选的格式修改器,以及用综括号括起来的转换字符与可选的参数。转换字符需要转换的字段。如:logger 的名字,日志级别,日期以及线程名。格式修改器控制字段的宽度,间距以及左右对齐。
我们这里举几个例子,完整的请移步官网:http://logback.qos.ch/manual/layouts.html#ClassicPatternLayout
作用 | pattern | 效果 |
---|---|---|
输出 logger 的名字 | c{length} lo{length} logger{length} | logger 名字会根据length长度被缩写,但是最右边的部分永远不会被简写,即使它的长度比 length 的值要大,其它的部分可能会被缩短为一个字符,但是永不会被移除。设置 length 的值为 0 返回 logger 名字中最右边的点右边的字符。 |
上下文名称 | contextName cn | 输出日志事件附加到的 logger 上下文的名字。 |
日期 | d{pattern} date{pattern} d{pattern, timezone} date{pattern, timezone} | 用于输出日志事件的日期。日期转换符允许接收一个字符串作为参数。字符串的语法与 SimpleDateFormat 中的格式完全兼容。 |
行号 | L / line | 输出发出日志请求所在的行号。 生成行号不是特别快,谨慎使用 |
日志信息 | m / msg / message | 输出与日志事件相关联的,由应用程序提供的日志信息。 |
换行 | n | 输出平台所依赖的行分割字符。 转换字符提供了像 "n" 或 "rn" 一样的转换效果。 |
日志级别 | p / le / level | 输出日志事件的级别。 |
线程名 | t / thread | 输出生成日志事件的线程名。 |
MDC | X{key:-defaultVal} mdc{key:-defaultVal} | 输出生成日志事件的线程的 MDC (mapped diagnostic context)。 如果 MDC 转换字符后面跟着用花括号括起来的 kye,例 %MDC{userid},那么 'userid' 所对应 MDC 的值将会输出。如果没有指定的 key,那么 MDC 的整个内容将会以 "key1=val1, key2=val2" 的格式输出。 |
类名 | C{length} class{length} | 输出发出日志请求的类的全限定名称。跟 %logger% 转换符一样。设置为0则不会打印包前缀。 生成调用者类的信息并不是特别快,谨慎使用 |
在给定的转换模式上下文中,% 有特殊的含义,如果作为字面量,需要进行转义。例如,"%d %p % %m%n"。
在大多数的情况下,字面量包括空格或者其它的分隔符,所以它们不会与转换字符混淆。例如,"%level [%thread] - %message%n" 包含字面量字符 " [" 与 "] - "。但是,如果一个转换字符后面紧跟着一个字面量,那么 logback 的模式解析器将会错误的认为这个字面量也是转换字符的一部分。例如,"%date%nHello" 将会被解析成两个转换字符 %date 与 %nHello,但是 %nHello 不是一个转换字符,所以 logback 将会输出 %PARSER_ERROR[nHello]。如果你想要区分 %n 跟 Hello,可以通过给 %n 传递一个空参数。例如,"%date%n{}Hello" 将会被解析为 %date %n 再紧跟着一个字符串 "Hello"。
格式修改器
有了pattern还不够,我们的每个日志的等级可能长可能短比如debug,info,就会造成日志打印的参差不齐,为了美观我们可以使用格式修改器,可以对每个数据字段进行对齐,以及更改最大最小宽度。
可选的格式修改器放在百分号跟转换字符之间。
第一个可选的格式修改器是左对齐标志,也就是减号 (-) 字符。接下来的是最小字段宽度修改器,它是一个十进制常量,表示输出至少多少个字符。如果字段包含很少的数据,它会选择填充左边或者右边,直到满足最小宽度。默认是填充左边 (右对齐),但是你可以通过左对齐标志来对右边进行填充。填充字符为空格。如果字段的数据大于最小字段的宽度,会自动扩容去容纳所有的数据。字段的数据永远不会被截断。
这个行为可以通过使用最大字段宽度修改器来改变,它通过一个点后面跟着一个十进制常量来指定。如果字段的数据长度大于最大字段的宽度,那么会从数据字段的开头移除多余的字符。
如果想从后面开始截断,可以在点后面增加一个减号。如果是这样的话,最大字段宽度是 8,数据长度是十个字符的长度,那么最后两个字符将会被丢弃。
格式修改器 | 左对齐 | 最小宽度 | 最大宽度 | 备注 |
---|---|---|---|---|
%20logger | false | 20 | none | 如果 logger 的名字小于 20 个字符的长度,那么会在左边填充空格 |
%-20logger | true | 20 | none | 如果 logger 的名字小于 20 个字符的长度,那么会在右边填充空格 |
%.30logger | NA | none | 30 | 如果 logger 的名字大于 30 个字符的长度,那么从前面开始截断 |
%20.30logger | false | 20 | 30 | 如果 logger 的名字大于 20 个字符的长度,那么会从左边填充空格。但是如果 logger 的名字大于 30 字符,将会从前面开始截断 |
%-20.30logger | true | 20 | 30 | 如果 logger 的名字小于 20 个字符的长度,那么从右边开始填充空格。但是如果 logger 的名字大于 30 个字符,将会从前面开始截断 |
%.-30logger | NA | none | 30 | 如果 logger 的名字大于 30 个字符的长度,那么从后面开始截断 |
我们来尝试解读一个pattern吧!
%d{yyyy/MM/dd HH:mm:ss.SSS} %-5p [%t] [%X{requestId} - %C{0}] %m%n
打印的格式大致是日期+左对齐5格的日志等级+ 被[]括起来的线程名+MDC定义的key为requestId+没有包前缀的类名+日志信息+类名。
我们所列举的都是比较常见的打印格式,还有更多奇妙的操作,如打印日志的颜色,分组格式化等等可以具体查看官方文档深入了解!