来源:logback.qos.ch/manual/enco... 作者:Ceki Gülcü、Sébastien Pennec、Carl Harris 版权 © 2000-2022 QOS.ch Sarl
本文档使用 创作共用署名 - 非商业 - 相同方式共享 2.5 许可证 授权。
ACTION THIS DAY Make sure they have all they want on extreme priority and report to me that this has been done.
即日行动 确保他们以极高的优先级得到所有需要的资源,并向我报告已完成情况。
------1941 年 10 月丘吉尔对哈斯廷斯·伊斯梅在回应艾伦·图灵和他在布莱切利园的密码分析同事要求更多资源的请求时所说的话。
为了运行本章中的示例,您需要确保某些 jar 文件存在于类路径中。请参考 设置页面 以了解更多详情。
什么是编码器
编码器负责将事件转换为字节数组,并将该字节数组写入 OutputStream 中。编码器是在 logback 版本 0.9.19 中引入的。在早期版本中,大多数追加器依靠布局将事件转换为字符串,并使用 java.io.Writer 将其写出。在 logback 的早期版本中,用户会在 FileAppender 中嵌套 PatternLayout。自 logback 0.9.19 起,FileAppender 和子类 期望有编码器而不再接受布局。
为什么要进行这种破坏性更改?
正如下一章详细讨论的那样,布局只能将事件转换为字符串,这限制了它们的范围仅限于非二进制输出。
编码器接口
编码器负责将传入的事件转换为字节数组。以下是 编码器接口:
java
package ch.qos.logback.core.encoder;
/**
* 编码器负责将传入的事件转换为字节数组
*/
public interface Encoder<E> extends ContextAware, LifeCycle {
/**
* 获取头部字节。此方法通常在打开输出流时调用。
*
* @return 头部字节。允许为空值。
*/
byte[] headerBytes();
/**
* 将事件编码为字节数组。
*
* @param event 事件。
*/
byte[] encode(E event);
/**
* 获取尾部字节。此方法通常在写入事件的流关闭之前调用。
*
* @return 尾部字节。允许为空值。
*/
byte[] footerBytes();
}
正如您所看到的,Encoder 接口由几个方法组成,但是这些方法可以实现许多有用的功能。
LayoutWrappingEncoder
直到 logback 版本 0.9.19,许多追加器都依赖于 Layout 实例来控制日志输出的格式。由于存在大量基于布局接口的代码,我们需要一种使编码器与布局互操作的方式。LayoutWrappingEncoder 填补了编码器和布局之间的差距。它实现了编码器接口并包装了一个布局,将事件转换为字符串的工作委托给该布局。
以下是 LayoutWrappingEncoder 类的摘录,说明如何执行到包装的布局实例的委托。
java
package ch.qos.logback.core.encoder;
public class LayoutWrappingEncoder<E> extends EncoderBase<E> {
protected Layout<E> layout;
private Charset charset;
// encode a given event as a byte[]
public byte[] encode(E event) {
String txt = layout.doLayout(event);
return convertToBytes(txt);
}
private byte[] convertToBytes(String s) {
if (charset == null) {
return s.getBytes();
} else {
return s.getBytes(charset);
}
}
}
doEncode 方法首先让包装的布局将传入的事件转换为字符串。根据用户选择的字符集编码,将得到的文本字符串转换为字节数组。
PatternLayoutEncoder
PatternLayoutEncoder 是 LayoutWrappingEncoder 的扩展,限制了对 PatternLayout 实例的包装,考虑到 PatternLayout 是最常用的布局,logback 为这种常见用例提供了支持。
自 logback 0.9.19 版本以来,每当配置 FileAppender 或其子类时,使用 PatternLayout 必须使用 PatternLayoutEncoder。这在 logback 错误代码 中有解释。
immediateFlush 属性
从 logback 1.2.0 版本开始,immediateFlush 属性是封闭的 Appender 的一部分。
将输出模式字符串作为头
为了方便解析日志文件,logback 可以在日志文件顶部插入用于日志输出的模式。默认情况下,此功能被禁用。可以通过为相关的 PatternLayoutEncoder 设置 outputPatternAsHeader 属性为 "true" 来启用此功能。以下是一个示例:
传统格式
xml
<configuration>
<!-- omitted lines ... -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>foo.log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d %-5level [%thread] %logger{0}: %msg%n</pattern>
<outputPatternAsHeader>true</outputPatternAsHeader>
</encoder>
</appender>
<!-- omitted lines ... -->
</configuration>
规范格式 (1.3)
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
<import class="ch.qos.logback.core.FileAppender"/>
<appender name="FILE" class="FileAppender">
<file>foo.log</file>
<encoder class="PatternLayoutEncoder">
<pattern>%d %-5level [%thread] %logger{0}: %msg%n</pattern>
<outputPatternAsHeader>true</outputPatternAsHeader>
</encoder>
</appender>
</configuration>
这将在日志文件中产生类似以下的输出:
accesslog
#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" 开头的一行是新插入的模式行。
JsonEncoder
==从版本 1.3.8/1.4.8 开始==
JsonEncoder 遵循 Newline delimited JSON (ndjson) 标准。它将日志事件转换为符合 RFC-8259 的有效 JSON 文本。JSON 文本格式中的每个日志事件后面跟随一个换行符。具体来说,根据 RFC-8259 第 7 节 中描述的规则,某些字符,如引号、反斜杠、正斜杠、换页符、换行符、回车字符被转义。
以下是经过格式化后的示例输出:
json
{
"sequenceNumber":0,
"timestamp":1686403686358,
"nanoseconds":358357100,
"level":"INFO",
"threadName":"main",
"loggerName":"org.foo.Bar",
"context":{
"name":"default",
"birthdate":1686403685679,
"properties":{
"moo":"299857071"
}
},
"mdc":{
"a1":"v1299857071"
},
"kvpList":[
{
"ik299857071":"iv299857071"
},
{
"a":"b"
}
],
"message":"Hello \"Alice\"",
"throwable":{
"className":"java.lang.RuntimeException",
"message":"an error",
"stepArray":[
{
"className":"org.foo.Bar",
"methodName":"httpCall",
"fileName":"Bar.java",
"lineNumber":293
},
{
"className":"jdk.internal.reflect.DirectMethodHandleAccessor",
"methodName":"invoke",
"fileName":"DirectMethodHandleAccessor.java",
"lineNumber":104
},
.... omitted lines
]
}
}
请注意,实际输出将更加密集,每行只有一个日志事件。
以下是使用 JsonEncoder 的示例配置文件。
传统格式
xml
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>foo.json</file>
<encoder class="ch.qos.logback.classic.encoder.JsonEncoder"/>
</appender>
<root>
<level value="DEBUG"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
规范格式 (1.3)
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.encoder.JsonEncoder"/>
<import class="ch.qos.logback.core.FileAppender"/>
<appender name="FILE" class="FileAppender">
<file>foo.json</file>
<encoder class="JsonEncoder"/>
</appender>
<root>
<level value="DEBUG"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
版权声明:本文为博主「佳佳」的原创文章,遵循 CC 4.0 BY-NC-SA 版权协议,转载请附上原文出处链接及本声明。