Encoder负责将logging event转换成byte array并将字节数组写入到一个OutputStream。在0.9.19版本之前,大多数appender依赖layout来将Logging event转回成字符串string ,然后用java.io.Writer写出。在之前的版本中,用户会在FileAppender中内建PatternLayout,但从0.9.19之后,FileAppender及其子类不在使用layout而采用encoder。
为什么要做成这样的改变呢?
Layout只能将event转换成string。此外,考虑到layout没有控制events写出的过程,所以Layout无法对events进行批处理操作。与之相比,encoder不仅能控制event转换成字节数组,还能控制字节数组写出到输出流的过程。
目前来说,PatternLayoutEncoder是唯一一个有用的encoder。它仅仅是简单包装了PatternLayout,主要的工作其实都是由内部的PatternLayout完成的。
下面我们看下Encoder interface
package ch.qos.logback.core.encoder;
public interface Encoder<E extends ContextAware, LifeCycle {
/**
* 该方法在appender初始化或者在输出流发生重定向的时候回调用,例如滚动日志发生的时候,
* RollingFileAppender
*/
void init(OutputStream os) throws IOException;
/**
* 将event编码以及写入到合适的输出流中。
* 实现可以自由地推迟将被编码event写出到输出流中的时间,而不是写在一个批处理当中。
* 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;
/**
* 该方法在关闭下属OutputStream前调用。它的实现必须不能主动关闭下属的OutputStream
* 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;
}
LayoutWrappingEncoder
在0.9.19之前,很多appenders依赖Layout格式化日志输出。因此就存在大象基于layout接口的代码,所以我们需要一种方法来兼容这些基于layout的代码。LayoutWrappingEncoder就在encoders和layout质检搭了一座桥。它既实现了encoder interface,又包装了Layout。
下面我们看看它的实现
package ch.qos.logback.core.encoder;
public class LayoutWrappingEncoder<E extends EncoderBase<E {
protected Layout<E layout;
private Charset charset;
private boolean immediateFlush = true;
public void doEncode(E event) throws IOException {
String txt = layout.doLayout(event);
outputStream.write(convertToBytes(txt));
if (immediateFlush)
outputStream.flush();
}
private byte[] convertToBytes(String s) {
if (charset == null) {
return s.getBytes();
} else {
return s.getBytes(charset);
}
}
}
整体很简单,先通过layout将event转换成string,然后转换成字节数组再通过与appender绑定的输出流输出字节数组。可以看到immediateFlush变量决定是否立刻刷新输出流缓冲区。默认为true。如果设置成false,可以显著提高日志的吞吐量。
下面看一个PatternLayoutEncoder的简单配置
考虑到PatternLayout是目前被广泛使用的layout,logbook为了迎合这种常见的使用情况,退出继承自LayoutWrappingEncoder,并且包装了PatternLayout实例的PatternLayoutEncoder。
在0.9.19版本中,FileAppender及其子类如果需要配置PatternLayout,就必须使用PatternLayoutEncoder来代替,否则会报错。更多配置错误可以查看
relevant entry in the logback error codes
.
将immediateFlush设置成false,可以通过缓存日志,再一次输出的方式,提供日志吞吐量
<appender name="FILE" class="ch.qos.logback.core.FileAppender"
<file>foo.log</file>
<encoder>
<pattern>%d %-5level [%thread] %logger{0}: %msg%n</pattern>
<!-- this quadruples logging throughput -->
<immediateFlush>false</immediateFlush>
</encoder>
</appender>
如果将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 - Hello world
2012-04-26 14:54:38,461 [main] DEBUG com.foo.App - Hi again