文章目录
- 一 What is an encoder
- 二 Encoder interface
- 三 LayoutWrappingEncoder
- 四 PatternLayoutEncoder
- 五 Layouts
- 六 PatternLayout
- 七
一 What is an encoder
Encoders
就是把 LoggingEvent
转化为字节数组并向 OutputStream
(输出流)写出的这么个东东.
Layouts
就是能够把 LoggingEvent
转换成字符串的东东.
相比较之下, Layouts
既不能控制 LoggingEvent
何时输出, 也不能把 LoggingEvent
聚合成批(batches). 但是 Encoder
既能控制 LoggingEvent
输出格式,也能控制 LoggingEvent
何时输出.
不过PatternLayoutEncoder
才是真正有用的encoder
. 它是对PatternLayout
的包装.
咋说呢, encoder
的抽象有点鸡肋,有啥不干脆把 layout
的职责也涵盖了算了?
二 Encoder interface
Encoder
有2个作用:
- 转化
LoggingEvent
为字节数组 - 将字节数组通过合适的
OutputStream
输出
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;
}
三 LayoutWrappingEncoder
LayoutWrappingEncoder
bridges(桥接) the gap between encoders and layouts. It implements the encoder interface and wraps a layout to which it delegates the work of transforming an event into string.
(注: LayoutWrappingEncoder
的出现本质上是历史设计缺陷, 其实 Layout
是没有必要单独拎出来的抽象, 而是完全可以作为 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);
}
}
}
四 PatternLayoutEncoder
PatternLayoutEncoder
是应用最为广泛 的layout
,也是对 LayoutWrappingEncoder
的扩展(扩展了PatternLayout
职责). 从 0.9.19 版本的logback 开始, FileAppender
及其子类只要配置PatternLayout
就一定会使用 PatternLayoutEncoder
.
五 Layouts
Layouts
就是把 LoggingEvent
格式化成字符串的组件(component).
我们看看接口定义:
public interface Layout<E> extends ContextAware, LifeCycle {
String doLayout(E event);
String getFileHeader();
String getPresentationHeader();
String getFileFooter();
String getPresentationFooter();
String getContentType();
}
在logback-classic里实际只会处理 ILoggingEvent 类型的 Event,而不会处理 AccessEvent类型的Event
六 PatternLayout
我们可以自己写一个 Layout
,但坦白说, 官方给出来的 Layout
已经能够满足日常需要了.往深了想, 打个日志而已, 把格式打出花来,那不也只是日志吗?
logback-classic 有一个很灵活的PatternLayout
,当然它的主要职责也是拿到一个logging event
,返回一个字符串.只不过这个字符串是被 PatternLayout
格式化的. 格式化就是所谓的conversion pattern
.
logback的 conversion pattern
和 C语言的printf 很类似.
尽管前面已经提到过, 但 FileAppender
及其子类需要一个Encoder
.比如常用的ConsoleAppender
就使用了PatternLayoutEncoder
.
我们看个栗子:
static public void main(String[] args) throws Exception {
Logger rootLogger = (Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
LoggerContext loggerContext = rootLogger.getLoggerContext();
// we are not interested in auto-configuration
loggerContext.reset();
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setContext(loggerContext);
encoder.setPattern("%-5level [%thread]: %message%n");
encoder.start();
ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<ILoggingEvent>();
appender.setContext(loggerContext);
appender.setEncoder(encoder);
appender.start();
rootLogger.addAppender(appender);
rootLogger.debug("Message 1");
rootLogger.warn("Message 2");
}
这里的conversion pattern
就是 %-5level [%thread]: %message%n
这一坨.