文章目录
一 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 这一坨.
七 Conversion word options
conversion specifier格式形如 %mdc{someKey}, 这个 specifier 能够获取 MDC中的K-V.
conversion specifier 还可以有多种 选项(option). 比如 conversion specifier还能够利用 自定义evluators 定制 pattern layout (当前最新版本 1.3.X 已经支持了吗?)
<pattern>%-4relative [%thread] %-5level - %msg%n %caller{2, DISP_CALLER_EVAL, OTHER_EVAL_NAME, THIRD_EVAL_NAME}</pattern>
八 Grouping with parentheses
使用括号 () 包起来的内容叫做"grouping tokens",比如:
%-30(%d{HH:mm:ss.SSS} [%thread]) %-5level %logger{32} - %msg%n
假如 "%d{HH:mm:ss.SSS} [%thread]"少于 30 各方字符就会自动填充, 这样日志看起来清爽很多,比如:
13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Classload hashcode is 13995234
13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Initializing for ServletContext
13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Trying platform Mbean server
13:09:30 [pool-1-thread-1] INFO ch.qos.logback.demo.LoggingTask - Howdydy-diddly-ho - 0
九 EventEvaluator
EventEvaluator 是个 java interface .它就是用来决定一个 LoggingEvent是否满足EventEvaluator的条件来决定是否输出这条日志的.
不过其实很少会这样用到. 假如我希望不输出这条日志,那么干啥调 logger.info这类API呢?
十 customer Converter
Converter是抽象类.
关于Converter抽象类的文档中解释是:
A minimal converter which sets up the general interface for derived classes. It also implements the functionality to chain converters in a linked list
说了等于没说.
其实Coverter的抽象很好理解,比如我希望在Pattern中动态加入一些自定义的属性(变量). 看这个例子它能记录 Converter创建的时间到记录日志时间差.
步骤一:
public class MySampleConverter extends ClassicConverter {
long start = System.nanoTime();
@Override
public String convert(ILoggingEvent event) {
long nowInNanos = System.nanoTime();
return Long.toString(nowInNanos-start);
}
}
步骤二:
<configuration>
<conversionRule conversionWord="nanos"
converterClass="chapters.layouts.MySampleConverter" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 注意这里的 %6-nanos -->
b <pattern>%-6nanos [%thread] - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
然后输出日志格式,所有日志前都带了纳秒时间:
4868695 [main] DEBUG - Everything's going well
5758748 [main] ERROR - maybe not quite...
另外,有一个MDCConverter 是比较特殊的.
十一 Logback access
11.1 PatternLayout
logback-access的PatternLayout 和 logback-classic十分类似; 但是 前者会有更多的 conversion specifiers, 尤其是因为 HTTP的 request/response而定制的 conversion specifiers.
比如:
| Conversion Word | feature |
|---|---|
| a / remoteIP | Remote IP address. |
| b / B / bytesSent | Response’s content length |
| h / clientHost | Remote host. |
| H / protocol | Request protocol. |
| r / requestURL | URL requested. |
| i{header} / header{header} | Request header. |
等等.
.
本文深入探讨Logback中的Encoder与Layout概念,包括PatternLayoutEncoder的使用方式与自定义转换器的方法,帮助读者理解如何高效地配置日志输出格式。
1780

被折叠的 条评论
为什么被折叠?



