Layouts
Layouts
1、什么是 layout?
layout 是 logback 的组件,负责将日志事件转换为字符串。Layout
接口中的 format()
方法接受一个表示日志事件的对象 (任何类型) 并返回一个字符串。Layout
接口的概要如下:
package org.apache.log4j;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.OptionHandler;
public abstract class Layout implements OptionHandler {
public static final String LINE_SEP = System.getProperty("line.separator");
public static final int LINE_SEP_LEN;
public Layout() {
}
public abstract String format(LoggingEvent var1);
public String getContentType() {
return "text/plain";
}
public String getHeader() {
return null;
}
public String getFooter() {
return null;
}
public abstract boolean ignoresThrowable();
static {
LINE_SEP_LEN = LINE_SEP.length();
}
}
2、格式修改器
默认情况下,相关信息按照原样输出。但是,在格式修改器的帮助下,可以对每个数据字段进行对齐,以及更改最大最小宽度。
可选的格式修改器放在百分号跟转换字符之间。
第一个可选的格式修改器是左对齐标志,也就是减号 (-) 字符。接下来的是最小字段宽度修改器,它是一个十进制常量,表示输出至少多少个字符。如果字段包含很少的数据,它会选择填充左边或者右边,直到满足最小宽度。默认是填充左边 (右对齐),但是你可以通过左对齐标志来对右边进行填充。填充字符为空格。如果字段的数据大于最小字段的宽度,会自动扩容去容纳所有的数据。字段的数据永远不会被截断。
这个行为可以通过使用最大字段宽度修改器来改变,它通过一个点后面跟着一个十进制常量来指定。如果字段的数据长度大于最大字段的宽度,那么会从数据字段的开头移除多余的字符。举个🌰,如果最大字段的宽度是 8,数据长度是十个字符的长度,那么开头的两个字符将会被丢弃。这个行为跟 C 语言中 printf 函数从后面开始截断的行为相违背。
如果想从后面开始截断,可以在点后面增加一个减号。如果是这样的话,最大字段宽度是 8,数据长度是十个字符的长度,那么最后两个字符将会被丢弃。
下面是各种格式修改器的例子:
格式修改器 | 左对齐 | 最小宽度 | 最大宽度 | 备注 |
---|---|---|---|---|
%20logger | false | 20 | none | 如果 logger 的名字小于 20 个字符的长度,那么会在左边填充空格 |
%-20logger | true | 20 | none | 如果 logger 的名字小于 20 个字符的长度,那么会在右边填充空格 |
%.30logger | NA | none | 30 | 如果 logger 的名字大于 30 个字符的长度,那么从前面开始截断 |
%20.30logger | false | 20 | 30 | 如果 logger 的名字大于 20 个字符的长度,那么会从左边填充空格。但是如果 logger 的名字大于 30 字符,将会从前面开始截断 |
%-20.30logger | true | 20 | 30 | 如果 logger 的名字小于 20 个字符的长度,那么从右边开始填充空格。但是如果 logger 的名字大于 30 个字符,将会从前面开始截断 |
%.-30logger | NA | none | 30 | 如果 logger 的名字大于 30 个字符的长度,那么从后面开始截断 |
下面的表格列出了格式修改器截断的例子。但是请注意综括号 "[]" 不是输出结果的一部分,它只是用来区分输出的长度。
格式修改器 | logger 的名字 | 结果 |
---|---|---|
[%20.20logger] | main.Name | [ main.Name] |
[%-20.20logger] | main.Name | [main.Name ] |
[%10.10logger] | main.foo.foo.bar.Name | [o.bar.Name] |
[%10.-10logger] | main.foo.foo.bar.Name | [main.foo.f] |
只输出日志等级的一个字符
除了可以输出 TRACE, DEBUG, WARN, INFO 或者 ERROR 来表示日志等级之外,还是输出T, D, W, I 与 E 来进行表示。你可以自定义转换器 或者利用刚才讨论的格式修改器来缩短日志级别为一个字符。这个转换说明符可能为 "%.-1level"。
3、转换字符的选项
一个转换字符后面可以跟一个选项。它们通过综括号来声明。我们之前已经看到了一些可能的选项。例如之前的 MDC 转换说明符 %mdc{someKey}。
一个转换说明符可能有多个可选项。一个转换说明符可以充分利用我们即将介绍到的 evaluator,可以添加多个 evaluator 的名字到可选列表。如下:
<pattern>%-4relative [%thread] %-5level - %msg%n \
%caller{2, DISP_CALLER_EVAL, OTHER_EVAL_NAME, THIRD_EVAL_NAME}</pattern>
如果这些选项中包含了一些特殊字符,例如花括号,空格,逗号。你可以使用单引号或者双引号来包裹它们。例如:
<pattern>%-5level - %replace(%msg){'\d{14,16}', 'XXXX'}%n</pattern>
我们传递 \d{16}
与 XXXX
给 replace
转换字符。它将消息中 14,15 或者 16 位的数字替换为 XXXX,用来混淆信用卡号码。在正则表达式中,"\d" 表示一个数字的简写。"{14,16}" 会被解析成 "{14,16}",也就是说前一个项将会被重复至少 14 次,至多 16 次。
4、特殊的圆括号
在 logback 里,模式字符串中的圆括号被看作为分组标记。因此,它能够对子模式进行分组,并且直接对子模式进行格式化。在 0.9.27 版本,logback 开始支持综合转换字符,例如 %replace 可以对子模式进行转换。
例如一下模式:
%-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
13:09:38 [btpool0-7] INFO c.q.l.demo.lottery.LotteryAction - Number: 50 was tried.
13:09:40 [btpool0-7] INFO c.q.l.d.prime.NumberCruncherImpl - Beginning to factor.
13:09:40 [btpool0-7] DEBUG c.q.l.d.prime.NumberCruncherImpl - Trying 2 as a factor.
13:09:40 [btpool0-7] INFO c.q.l.d.prime.NumberCruncherImpl - Found factor 2
如果对 "%-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
13:09:38 [btpool0-7] INFO c.q.l.demo.lottery.LotteryAction - Number: 50 was tried.
13:09:40 [btpool0-7] INFO c.q.l.d.prime.NumberCruncherImpl - Beginning to factor.
13:09:40 [btpool0-7] DEBUG c.q.l.d.prime.NumberCruncherImpl - Trying 2 as a factor.
13:09:40 [btpool0-7] INFO c.q.l.d.prime.NumberCruncherImpl - Found factor 2
后者的格式更加容易阅读。
如果你想将圆括号当作字面量输出,那么你需要对每个圆括号用反斜杠进行转义。就像 (%d{HH:mm:ss.SSS} [%thread]) 一样。
5、着色
如上所述的圆括号分组,允许对子模式进行着色。在 1.0.5 版本,PatternLayout
可以识别 "%black","%red","%green","%yellow","%blue","%magenta","%cyan", "%white", "%gray", "%boldRed","%boldGreen", "%boldYellow", "%boldBlue", "%boldMagenta""%boldCyan", "%boldWhite" 以及 "%highlight" 作为转换字符。这些转换字符都还可以包含一个子模式。任何被颜色转换字符包裹的子模式都会通过指定的颜色输出。
PatternLayout | 描述 |
---|---|
black | 黑色 |
red | 红色 |
green | 绿色 |
yellow | 黄色 |
blue | 蓝色 |
magenta | 品红 |
cyan | 青色 |
white | 白色 |
gray | 灰色 |
boldRed | |
boldGreen | |
boldYellow | |
boldBlue | |
boldMagenta | |
boldCyan | |
boldWhite | |
highlight |
面是关于着色的配置文件。
<configuration debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- 在 Windows 平台下,设置 withJansi = true 来开启 ANSI 颜色代码需要 Jansi 类库 -->
<!-- 需要在 classpath 引入 org.fusesource.jansi:jansi:1.8 包 -->
<!-- 在基于 Unix 操作系统,像 Linux 以及 Mac OS X 系统默认支持 ANSI 颜色代码 -->
<withJansi>true</withJansi>
<encoder>
<pattern>[%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg %n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
5.1、Spring 提供的转换器
ColorConverter 源码
public class ColorConverter extends CompositeConverter<ILoggingEvent> {
private static final Map<String, AnsiElement> ELEMENTS;
static {
Map<String, AnsiElement> ansiElements = new HashMap<>();
ansiElements.put("black", AnsiColor.BLACK);
ansiElements.put("white", AnsiColor.WHITE);
ansiElements.put("faint", AnsiStyle.FAINT);
ansiElements.put("red", AnsiColor.RED);
ansiElements.put("green", AnsiColor.GREEN);
ansiElements.put("yellow", AnsiColor.YELLOW);
ansiElements.put("blue", AnsiColor.BLUE);
ansiElements.put("magenta", AnsiColor.MAGENTA);
ansiElements.put("cyan", AnsiColor.CYAN);
ansiElements.put("bright_black", AnsiColor.BRIGHT_BLACK);
ansiElements.put("bright_white", AnsiColor.BRIGHT_WHITE);
ansiElements.put("bright_red", AnsiColor.BRIGHT_RED);
ansiElements.put("bright_green", AnsiColor.BRIGHT_GREEN);
ansiElements.put("bright_yellow", AnsiColor.BRIGHT_YELLOW);
ansiElements.put("bright_blue", AnsiColor.BRIGHT_BLUE);
ansiElements.put("bright_magenta", AnsiColor.BRIGHT_MAGENTA);
ansiElements.put("bright_cyan", AnsiColor.BRIGHT_CYAN);
ELEMENTS = Collections.unmodifiableMap(ansiElements);
}
开启 clr 转换规则
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
配置
<pattern>%-250(${CONTEXT_NAME} ${ip} %clr(%d{HH:mm:ss.SSS}){red} %clr(${LOG_LEVEL_PATTERN:-%-5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr(%applicationName[%-15.15t]){faint} %clr(%-40.40logger{39}){cyan} %boldYellow(%M) %clr(:){bright_yellow} ) %m%n%wEx</pattern>
6、自定义转换说明符
定义的转换字符需要两步
第一步
首先,你必须继承 ClassicConverter
类。ClassicConverter
对象负责从 ILoggingEvent
实例中抽取信息并输出字符串。例如,%logger 对应的转换器 LoggerConverter
,可以从 ILoggingEvent
从抽取 logger 的名字,返回一个字符串。它可以缩写 logger 的名字。
下面是一个自定义的转换器,返回从创建开始经过的时间,单位为纳秒。
Example: NanoConverter
import ch.qos.logback.classic.pattern.ClassicConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
public class NanoConverter extends ClassicConverter {
long start = System.nanoTime();
@Override
public String convert(ILoggingEvent event) {
long nowInNanos = System.nanoTime();
return Long.toString(nowInNanos - start);
}
}
这个实现非常简单。NanoConverter
继承了 ClassicConverter
并实现了 convert
方法,返回从创建开始经过多少纳秒。
第二步
我们必须让 logback 知道这个新建的 Converter
。所以我们需要在配置文件中进行声明,如下:
<configuration>
<!-- 自定义转换说明符-->
<conversionRule conversionWord="nanos"
converterClass="com.jin.logging.converter.NanoConverter"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-6nanos [%thread] - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
输出效果
26113953 [main] - Everything's going well
26672034 [main] - maybe not quite...
7、HTMLLayout
HTMLLayout
(包含在 logback-classic 中) 以 HTML 格式生成日志。HTMLLayout
通过 HTML 表格输出日志,每一行对应一条日志事件。
表格的列是通过转换模式指定的。关于转换模式的文档请查看 PatternLayout。所以,你可以完全控制表格的内容以及格式。你可以选择并且展示任何跟 PatternLayout
组合的转换器。
<configuration debug="true">
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>%d{MM-dd HH:mm:ss.SSS}%thread%5level%logger{50}%msg%n</pattern>
</layout>
</encoder>
<file>test.html</file>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
注意事项:不要使用空格或者其它的字面量来分隔转换说明符。转换模式中的每个说明符都会被当做一个单独的列。同样的转换模式中的每个文本块也会被当作一个单独的列,这会占用屏幕的空间。
空格效果对比
无空格 pattern 表达式
<pattern>%d{MM-dd HH:mm:ss.SSS}%thread%5level%logger{50}%msg%n</pattern>
效果如下:

有空格 pattern 表达式
<pattern>%d{MM-dd HH:mm:ss.SSS} %thread %5level %logger{50} %msg%n</pattern>
效果如下:

8、其他转换说明符
%clr 使用%clr转换字配置颜色编码 如:%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){yellow}
%c 输出logger名称
%C 输出类名
%d{HH:mm:ss.SSS} 表示输出到毫秒的时间
%t 输出当前线程名称
%-5level 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0
%logger 输出logger名称,因为Root Logger没有名称,所以没有输出
%msg 日志文本
%n 换行
其他常用的占位符有:
%F 输出所在的类文件名,如Log4j2Test.java
%L 输出行号
%M或%method 输出所在方法名
%l 输出完整的错误位置, 包括类名、方法名、文件名、行数
%p 该条日志的优先级
%replace{pattern}{regex}{substitution} 将pattern的输出结果pattern按照正则表达式regex替换成substitution