logback-日志配置

1 日志级别

优先级 日志级别 描述
1 trace 追踪,指明程序运行轨迹
2 debug 调试,实际应用中一般将其作为最低级别
3 info 输出重要的信息,使用较多
4 warn 警告
5 error 错误

2 格式

输出格式 描述
%date{yyyy-MM-dd HH:mm:ss.SSS} 日志生产时间,精确到毫秒
%-5level 日志级别。例如 -5 表示左对齐并且固定输出 5 个字符,如果不足在右边补 0(ILoggingEvent.getLevel方法返回值)
%logger logger 的名称,例如 logger{36} 表示 logger 名字最长 36 个字符(ILoggingEvent.getLoggerName方法返回值)
%thread 输出当前线程名称(ILoggingEvent.getThreadName方法返回值)
%p 日志输出格式
%msg 日志内容
%n 换行符
%class 输出 java 类名
%file 输出文件名
%L 输出错误行号
%method 输出方法名
%l 输出语句所在的行数, 包括类名、方法名、文件名、行数
hostName 本地机器名
hostAddress 本地 ip 地址

3 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 定义日志输出格式和存储路径-->
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS}\t[%thread]\t%-5level %logger{36} - %msg%n"/>
<!-- 注:如果最后打成jar包,日志将输出在logs目录下 -->
<property name="INFO_PATH" value="logs/info.%d{yyyy-MM-dd}.%i.log"/>
<property name="WARN_ERR_PATH" value="logs/warn_err.%d{yyyy-MM-dd}.%i.log"/>

<!-- 定义控制台输出 -->
<!-- 注1:如果最后打成jar包,该部分输出会出现在jar包所在目录下的sout.log文件 -->
<!-- 注2:name属性命名随意,引用时对应即可 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 打印格式 -->
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>

<!-- info类型配置(仅显示info,过滤warn、error)-->
<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- LevelFilter,按日志等级过滤 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
</filter>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>DENY</onMatch>
</filter>
<!-- 自定义过滤器 -->
<filter class="com.xx.LogbackFilter"/>

<!-- 文件名样式、保存周期、文件大小 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${INFO_PATH}</fileNamePattern>
<maxHistory>3</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>5MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>

<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>

<!--WARN、ERROR类型配置(仅显示warn及其以上等级)-->
<appender name="FILE_WARN_ERR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${WARN_ERR_PATH}</fileNamePattern>
<maxHistory>10</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>

<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>

<!-- turboFilter类型的过滤器比appender先触发 -->
<turboFilter class="ch.qos.logback.classic.turbo.DynamicThresholdFilter">
<DefaultThreshold>ERROR</DefaultThreshold>
<OnHigherOrEqual>ACCEPT</OnHigherOrEqual>
<OnLower>NEUTRAL</OnLower>
<Key>cando</Key>

<MDCValueLevelPair>
<value>read</value>
<level>DEBUG</level>
</MDCValueLevelPair>

<MDCValueLevelPair>
<value>write</value>
<level>INFO</level>
</MDCValueLevelPair>

<MDCValueLevelPair>
<value>all</value>
<level>WARN</level>
</MDCValueLevelPair>
</turboFilter>

<!-- 根目录输出等级为DEBUG -->
<root level="DEBUG">
<appender-ref ref="FILE_INFO"/>
<appender-ref ref="FILE_WARN_ERR"/>
</root>
</configuration>

3.1 过滤器

logbcak 允许给日志记录器 appender 配置一个或多个 Filter,或者给整体配置一个或多个 TurboFilter 实现当满足过滤器指定的条件时处理日志。

过滤器 类型 描述
LevelFilter Filter 对等于(onMatch)或不等于(onMismatch)指定 level 的日志进行处理
ThresholdFilter Filter 对大于或等于(onMatch)指定 level 的日志进行处理;小于(onMismatch)指定 level 的日志进行处理
EvaluatorFilter Filter 对满足(onMatch)或不满足(onMismatch)指定表达式的日志进行处理
MDCFilter TurboFilter 对等于(onMatch)或不等于(onMismatch) MDCKey 及其 Value 的日志进行处理
DuplicateMessageFilter TurboFilter 不记录多余的重复的日志。有两个子标签:<cacheSize> 表示内部缓存对旧消息引用的个数上限,默认 100;<allowedRepetitions> 表示允许消息出现的重复次数上限,超过次数上限的记录请求将被丢弃
DynamicThresholdFilter TurboFilter 动态版的 ThresholdFilter,根据 MDC 域中是否存在某个键,该键对应的值是否相等,可实现日志级别动态切换
MarkerFilter TurboFilter 对带有指定标记的日志进行处理

onMatch、onMismatch 的三种取值和处理方式:

  • DENY:拒绝了记录
  • NEUTRAL:本级过滤器放行,不记录。注意,如果日志途径的所有过滤器都是 NEUTRAL,则记录
  • ACCEPT:需要记录

EvaluatorFilter 例子

<appender> 标签中使用,需要额外引入依赖:

1
2
// https://mvnrepository.com/artifact/org.codehaus.janino/janino
implementation group: 'org.codehaus.janino', name: 'janino', version: '3.1.9'

当前需要拒绝来自 org.apache.http.wire 类和 org.apache.http.headers 类的日志信息,其他日志记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator>
<expression>
if(event.getLoggerName().contains("org.apache.http.wire") || event.getLoggerName().contains("org.apache.http.headers")){
return true;
} else {
return false;
}
</expression>
</evaluator>
<OnMatch>DENY</OnMatch>
<OnMismatch>ACCEPT</OnMismatch>
</filter>

MDCFilter 例子

例如,仅记录 MDCKey 为 cando,Value 为 read 的日志:

1
2
3
4
5
6
<turboFilter class="ch.qos.logback.classic.turbo.MDCFilter">
<MDCKey>cando</MDCKey>
<Value>read</Value>
<OnMatch>ACCEPT</OnMatch>
<onMismatch>DENY</onMismatch>
</turboFilter>

DuplicateMessageFilter 例子

限制仅显示 1 条日志

1
2
3
<turboFilter class="ch.qos.logback.classic.turbo.DuplicateMessageFilter">
<AllowedRepetitions>1</AllowedRepetitions>
</turboFilter>

测试代码:

1
2
3
4
log.info(marker, "this is marker");
log.info("this is marker");
log.info(String.format("this is marker", 1));
log.info(String.format("this is marker", 2));

测试代码中第 2-4 行判断为重复,输出结果:

1
2
16:52:01.698	[Test worker]	INFO  s.ApplicationTests - this is marker
16:52:01.699 [Test worker] INFO s.ApplicationTests - this is marker

DynamicThresholdFilter 例子`

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
MDC.put("null", "none");
log.trace("key={ null },value={ none },level={ TRACE }");
log.debug("key={ null },value={ none },level={ DEBUG }");
log.info("key={ null },value={ none },level={ INFO }");
log.warn("key={ null },value={ none },level={ WARN }");
log.error("key={ null },value={ none },level={ ERROR }");

MDC.put("cando", "read");
log.trace("key={ cando },value={ read },level={ TRACE }");
log.debug("key={ cando },value={ read },level={ DEBUG }");
log.info("key={ cando },value={ read },level={ INFO }");
log.warn("key={ cando },value={ read },level={ WARN }");
log.error("key={ cando },value={ read },level={ ERROR }");

MDC.put("cando", "write");
log.trace("key={ cando },value={ write },level={ TRACE }");
log.debug("key={ cando },value={ write },level={ DEBUG }");
log.info("key={ cando },value={ write },level={ INFO }");
log.warn("key={ cando },value={ write },level={ WARN }");
log.error("key={ cando },value={ write },level={ ERROR }");

MDC.put("cando", "none");
log.trace("key={ cando },value={ none },level={ TRACE }");
log.debug("key={ cando },value={ none },level={ DEBUG }");
log.info("key={ cando },value={ none },level={ INFO }");
log.warn("key={ cando },value={ none },level={ WARN }");
log.error("key={ cando },value={ none },level={ ERROR }");

MDC.put("cando", "all");
log.trace("key={ cando },value={ all },level={ TRACE }");
log.debug("key={ cando },value={ all },level={ DEBUG }");
log.info("key={ cando },value={ all },level={ INFO }");
log.warn("key={ cando },value={ all },level={ WARN }");
log.error("key={ cando },value={ all },level={ ERROR }");

MDC.put("null", "none");
log.trace("key={ null },value={ none },level={ TRACE }");
log.debug("key={ null },value={ none },level={ DEBUG }");
log.info("key={ null },value={ none },level={ INFO }");
log.warn("key={ null },value={ none },level={ WARN }");
log.error("key={ null },value={ none },level={ ERROR }");

执行结果:

  • 当 key 和 value 都对应时,记录大于等于给定 level 的日志
  • 当 key 对应,value 不对应时,按<DefaultThreshold>标签处理
  • 当 key 不对应时,无论 value 是否对应,所得结果不确定:在首位调用,仅记录ERROR,满足<DefaultThreshold>标签限定;若紧跟其他调用,会与该调用的等级相同。
1
2
3
4
5
6
7
8
9
10
11
12
13
13:41:52.810	[Test worker]	ERROR s.ApplicationTests - key={ null },value={ none },level={ ERROR }
13:41:52.811 [Test worker] DEBUG s.ApplicationTests - key={ cando },value={ read },level={ DEBUG }
13:41:52.811 [Test worker] INFO s.ApplicationTests - key={ cando },value={ read },level={ INFO }
13:41:52.811 [Test worker] WARN s.ApplicationTests - key={ cando },value={ read },level={ WARN }
13:41:52.811 [Test worker] ERROR s.ApplicationTests - key={ cando },value={ read },level={ ERROR }
13:41:52.811 [Test worker] INFO s.ApplicationTests - key={ cando },value={ write },level={ INFO }
13:41:52.811 [Test worker] WARN s.ApplicationTests - key={ cando },value={ write },level={ WARN }
13:41:52.811 [Test worker] ERROR s.ApplicationTests - key={ cando },value={ write },level={ ERROR }
13:41:52.811 [Test worker] ERROR s.ApplicationTests - key={ cando },value={ none },level={ ERROR }
13:41:52.811 [Test worker] WARN s.ApplicationTests - key={ cando },value={ all },level={ WARN }
13:41:52.811 [Test worker] ERROR s.ApplicationTests - key={ cando },value={ all },level={ ERROR }
13:41:52.811 [Test worker] WARN s.ApplicationTests - key={ null },value={ none },level={ WARN }
13:41:52.811 [Test worker] ERROR s.ApplicationTests - key={ null },value={ none },level={ ERROR }

MarkerFilter 例子

指定记录带有 test 标记的日志:

1
2
3
4
5
<turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
<Marker>test</Marker>
<OnMatch>ACCEPT</OnMatch>
<OnMismatch>DENY</OnMismatch>
</turboFilter>

测试代码:

1
2
3
4
Marker marker = MarkerFactory.getMarker("test");

log.info(marker,"this is marker");
log.info("a normal log");

3.2 自定义过滤器

实现一个与上文相同的功能,拒绝来自 org.apache.http.wire 类和 org.apache.http.headers 类的日志信息,其他日志记录:

1
2
3
4
5
6
7
8
9
10
public class LogbackFilter extends Filter<ILoggingEvent> {

@Override
public FilterReply decide(ILoggingEvent event) {
if(event.getLoggerName().contains("org.apache.http.wire") || event.getLoggerName().contains("org.apache.http.headers")){
return FilterReply.DENY;
}
return FilterReply.NEUTRAL;
}
}