On this page
第 5 章编码器
**** 这天采取行动**确保他们拥有他们想要的所有优先事项,并向我报告已经完成。
— 1941 年 10 月,为了回应艾伦·图灵(Alan Turing)和他在布莱奇利公园(Bletchley Park)的密码分析家同事签署的更多资源请求,致使黑斯廷斯·伊斯梅(将军)教堂
什么是编码器
编码器负责将事件转换为字节数组,并将该字节数组写出为OutputStream
。编码器是在 logback 版本 0.9.19 中引入的。在以前的版本中,大多数追加程序都依赖于布局将事件转换为字符串并使用java.io.Writer
将其写出。在以前的版本中,用户会将PatternLayout
嵌套在FileAppender
内。自从 0.9.19 重新logback以来,FileAppender
和子类期待编码器,不再需要布局。
为什么要进行重大变革?
布局将在下一章中详细讨论,它只能将事件转换为字符串。此外,由于布局无法控制何时写入事件,因此布局无法将事件聚合为批。与此相比,编码器不仅可以完全控制要写出的字节的格式,还可以控制何时(以及是否)可以写出这些字节。
目前,PatternLayoutEncoder
是唯一 true 有用的编码器。它只包装一个完成大多数工作的PatternLayout
。因此,似乎除了不必要的复杂性之外,编码器并没有带来太多好处。但是,我们希望随着新型强大编码器的出现,这种印象会改变。
Encoder interface
编码器负责将进入的事件转换为字节数组**,并将结果字节数组写到适当的OutputStream
上。因此,编码器完全控制什么字节以及何时将字节写入拥有者所维护的OutputStream
。这是Encoder interface:
package ch.qos.logback.core.encoder;
public interface Encoder<E> extends ContextAware, LifeCycle {
/**
* This method is called when the owning appender starts or whenever output
* needs to be directed to a new OutputStream, for instance as a result of a
* rollover.
*/
void init(OutputStream os) throws IOException;
/**
* Encode and write an event to the appropriate {@link OutputStream}.
* Implementations are free to defer writing out of the encoded event and
* instead write in batches.
*/
void doEncode(E event) throws IOException;
/**
* This method is called prior to the closing of the underling
* {@link OutputStream}. Implementations MUST not close the underlying
* {@link OutputStream} which is the responsibility of the owning appender.
*/
void close() throws IOException;
}
如您所见,Encoder
接口由几种方法组成,但是令人惊讶的是,使用这些方法可以完成许多有用的事情。
LayoutWrappingEncoder
在 0.9.19 版本的 Logback 之前,许多附加程序都依赖 Layout 实例来控制日志输出的格式。由于存在大量基于布局接口的代码,因此我们需要一种编码器与布局互操作的方法。 LayoutWrappingEncoder弥合了编码器和布局之间的差距。它实现了编码器接口,并包装了一个布局,该布局委派了将事件转换为字符串的工作。
以下是LayoutWrappingEncoder
类的摘录,说明了如何委派给包装的布局实例。
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
鉴于PatternLayout
是最常用的布局,因此 logback 通过PatternLayoutEncoder
满足了这种常见用例,LayoutWrappingEncoder
的扩展仅限于包装PatternLayout
的实例。
从 0.9.19 版开始,无论何时FileAppender
或其子类之一配置了PatternLayout
,都必须使用PatternLayoutEncoder
来代替。 logback错误代码中的相关条目中对此进行了说明。
immediateFlush property
从 1.2.0 版开始,backupFlush 属性是封闭的 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 行。