Encoders
就是把 LoggingEvent
转化为字节数组并向 OutputStream
(输出流)写出的这么个东东.
Layouts
就是能够把 LoggingEvent
转换成字符串的东东.
相比较之下, Layouts
既不能控制 LoggingEvent
何时输出, 也不能把 LoggingEvent
聚合成批(batches). 但是 Encoder
既能控制 LoggingEvent
输出格式,也能控制 LoggingEvent
何时输出.
不过PatternLayoutEncoder
才是真正有用的encoder
. 它是对PatternLayout
的包装.
咋说呢, encoder
的抽象有点鸡肋,有啥不干脆把 layout
的职责也涵盖了算了?
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
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
是应用最为广泛 的layout
,也是对 LayoutWrappingEncoder
的扩展(扩展了PatternLayout
职责). 从 0.9.19 版本的logback 开始, FileAppender
及其子类只要配置PatternLayout
就一定会使用 PatternLayoutEncoder
.
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
我们可以自己写一个 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
这一坨.