Log4j 2 (5) Layout与Filter
Appender使用布局将LogEvent格式化为可以让日志的输出更具有可读性。 在Log4j 1.x和Logback Layout中,都是把日志事件转成String。 在Log4j 2中,Layout返回一个字节数组。 这样可以让Layout的结果输出形式更加多样化。 但是,这也意味着我们需要为大多数Layout配置一个字符集,以确保字节数组能够正确输出。
如果你仔细阅读官方文档,你会发现,log4j比logback的输出更加丰富,可以支持CSV,JSON,XML等等,这些对于在web项目中可能也不常用,我也不做记录,如果将来真的需要,直接阅读官方文档以及示例。这里我们关注我们用的最频繁的Pattern Layout。
Pattern Layout
这个类型的layout最灵活也是最常用
参数列表:
Parameter Name | Type | Description |
---|---|---|
charset | String | 输出字符串为字节数组时使用的编码方式,如果不指定使用平台默认编码 |
pattern | String | 指定如何转换LogEvent为字符串的模式 |
patternSelector | PatternSelector | 根据LogEvent选择一种Pattern, pattern和patternSelector参数互斥 |
replace | RegexReplacement | 允许替换部分结果字符串。 如果已配置,则replace元素必须指定要匹配的正则表达式和替换。 |
alwaysWriteExceptions | boolean | 如果你不再pattern中指定异常输出,则使用默认异常格式,附加在Pattern尾部 |
Patterns规则
%项 | 说明 |
---|---|
c{precision}/logger{precision} | 输出logger的名称,可选的指定一个精度:当精度说明符为整数值时,它将减小记录器名称的大小。 如果数字为正,则布局将打印相应数量的最右边记录器名称组件。 如果为负,则布局将删除相应数量的最左边的记录器名称组件。 如果精度包含任何非整数字符,则布局会根据模式缩写名称。 如果精度整数小于1,则布局仍将完整打印最右边的标记。 默认情况下,布局将完整打印记录器名称。 |
C{precision}/class{precision} | 输出发起日志记录请求的那个类的全限定名称。生成调用方的类名称(位置信息)成本昂贵,并且可能会影响性能。 |
d{pattern}/date{pattern} | 输出记录事件的日期。 日期转换说明符后可以跟一组大括号,每个大括号包含每个SimpleDateFormat的日期和时间模式字符串。 |
highlight{pattern}{style} | 添加ANSI颜色高亮。【Linux才可以显示颜色】默认针对各种级别的高亮颜色为:FATAL、ERROR、WARN、INFO、DEBUG、TRACEpattern为需要高亮的其它Pattern字段style取值可以是Default或者Logback。后者亮度较高 |
K{key}/map{key}/MAP{key} | 用于输出MapMessage中的条目 |
l/location | 输出调用者的位置信息,包括方法名和行号,对性能有影响,谨慎使用 |
L/line | 输出调用发起处的行号,对性能有影响,谨慎使用 |
M/method | 输出调用发起处的方法名,对性能有影响,谨慎使用 |
marker | 输出Marker的完整名称,如果由父Marker也输出 |
n | 输出平台相关的换行符 |
N/nano | 输出System.nanoTime() |
pid{[defaultValue]}/processId{[defaultValue]} | 输出PID |
p/level{level=label, level=label, ...} | 输出日志的级别,示例:%level{WARN=Warning, DEBUG=Debug, ERROR=Error, TRACE=Trace, INFO=Info} %level{WARN=W, DEBUG=D, ERROR=E, TRACE=T, INFO=I} |
r | 输出自JVM启动依赖流逝的毫秒数 |
sn/sequenceNumber | 输出此日志事件的序列号 |
T/tid/threadId | 输出当前线程ID |
t/tn/thread/threadName | 输出当前线程名称 |
tp/threadPriority** | 输出当前线程优先级 |
X{key[,key2...]}<br/>mdc{key[,key2...]}<br/>MDC{key[,key2...]} | 输出与生成日志事件的线程关联的线程上下文映射(也称为映射诊断上下文或MDC) |
x NDC | 输出与生成日志事件的线程关联的线程上下文堆栈(也称为嵌套诊断上下文或NDC) |
当然还有一些不常用的不列举出来,具体看官方的表格。基本上与logback的encoder差不多的语法
logger pattern示例
Conversion Pattern | Logger Name | Result |
---|---|---|
%c{1} | org.apache.commons.Foo | Foo |
%c{2} | org.apache.commons.Foo | commons.Foo |
%c{10} | org.apache.commons.Foo | org.apache.commons.Foo |
%c{-1} | org.apache.commons.Foo | apache.commons.Foo |
%c{-2} | org.apache.commons.Foo | commons.Foo |
%c{-10} | org.apache.commons.Foo | org.apache.commons.Foo |
%c{1.} | org.apache.commons.Foo | o.a.c.Foo |
%c{1.1.~.~} | org.apache.commons.test.Foo | o.a.~.~.Foo |
%c{.} | org.apache.commons.test.Foo | ....Foo |
date pattern示例
Pattern | Example |
---|---|
%d{DEFAULT} | 2012-11-02 14:34:02,123 |
%d{DEFAULT_MICROS} | 2012-11-02 14:34:02,123456 |
%d{DEFAULT_NANOS} | 2012-11-02 14:34:02,123456789 |
%d{ISO8601} | 2012-11-02T14:34:02,781 |
%d{ISO8601_BASIC} | 20121102T143402,781 |
%d{ISO8601_OFFSET_DATE_TIME_HH} | 2012-11-02'T'14:34:02,781-07 |
%d{ISO8601_OFFSET_DATE_TIME_HHMM} | 2012-11-02'T'14:34:02,781-0700 |
%d{ISO8601_OFFSET_DATE_TIME_HHCMM} | 2012-11-02'T'14:34:02,781-07:00 |
%d{ABSOLUTE} | 14:34:02,781 |
%d{ABSOLUTE_MICROS} | 14:34:02,123456 |
%d{ABSOLUTE_NANOS} | 14:34:02,123456789 |
%d{DATE} | 02 Nov 2012 14:34:02,781 |
%d{COMPACT} | 20121102143402781 |
%d{UNIX} | 1351866842 |
%d{UNIX_MILLIS} | 1351866842781 |
Pattern | Example |
---|---|
%d{HH:mm:ss,SSS} | 14:34:02,123 |
%d{HH:mm:ss,nnnn} to %d{HH:mm:ss,nnnnnnnnn} | 14:34:02,1234 to 14:34:02,123456789 |
%d{dd MMM yyyy HH:mm:ss,SSS} | 02 Nov 2012 14:34:02,123 |
%d{dd MMM yyyy HH:mm:ss,nnnn} to %d{dd MMM yyyy HH:mm:ss,nnnnnnnnn} | 02 Nov 2012 14:34:02,1234 to 02 Nov 2012 14:34:02,123456789 |
%d{HH:mm:ss}{GMT+0} | 18:34:02 |
我们来尝试解读一个pattern吧!
%d{yyyy/MM/dd HH:mm:ss.SSS} %-5p [%t] [%X{requestId} - %C{0}] %m%n
打印的格式大致是日期+左对齐5格的日志等级+ 被[]括起来的线程名+MDC定义的key为requestId+没有包前缀的类名(指的是logger的名)+日志信息。
Filter
Log4j 2的过滤器其实和logback也是差不多的,我们直接引用logback中关于过滤器的描述:
logback 过滤器基于三元逻辑(ternary logic ),允许它们有序地组装或者链接在一起组成一个任意复杂的过滤策略。那么你可能就会问了:哎呀什么是三元逻辑呢?跟Java的三元运算符概念差不多吗?答案当然是跟三元运算符不一样了,它更偏向是二元逻辑,只有真假两种。我们来看一下维基百科上对三元逻辑的解释吧:
有三种状态来表示真、假和一个表示不确定的第三值;;这相对于基础的二元逻辑(比如布尔逻辑,它只提供真假两种状态)
我们现在知道了原来三元逻辑就是有三种状态啊,既然是基于三元逻辑的过滤器应该也有三个类似的状态值吧?是的,没错!过滤器的
decide(ILoggingEvent event)
方法会被调用,且返回值只能是FilterReply
中定义的ACCEPT
、DENY
和NEUTRAL
的三个枚举值的其中一个。如果返回DENY,那么日志事件立即被抛弃,不再经过剩余过滤器;
如果返回NEUTRAL,那么有序列表里的下一个过滤器会接着处理记录事件;
如果返回ACCEPT,那么日志事件被立即处理,不再经过剩余过滤器。
在log4j中也是使用这三个状态代表过滤状态。可以在以下四个位置之一中配置过滤器::
- 在配置文件里直接配置
Context-wide Filters
。这个过滤器是全局范围的过滤器,即如果被这个过滤器拒绝的日志将不会到下一个过滤器进行处理,相反如果被这个过滤器接受的将直接执行,不会进入到其他过滤器甚至不会受到日志等级的影响。他被配置在Appender
和Logger
中; - 在
Logger
配置Filters
。这个类型的过滤器执行顺序是排在Context-wide Filters
和日志等级的过滤之后。这些过滤器拒绝的事件都将被丢弃,并且该事件不会传递给父Logger
,即使additivity
设置为true
; - 在
Appender
中配置Filters
。用于确定特定的Appender
是否应处理日志事件的格式化和输出; - 在
Appender Reference
中配置的Filters。用于确定Logger是否应将事件路由到Appender
。
举个例子,表示下以上四个位置的过滤器:
<Configuration status="warn" name="MyApp" packages="">
<Filters>
<!-- 全局级别Filter-> Context-wide Filters -->
<LevelRangeFilter minLevel="DEBUG" maxLevel="ERROR" onMatch="DENY"></LevelRangeFilter>
</Filters>
<Loggers>
<!-- Logger级别的Filter -->
<Root level="error">
<MapFilter onMatch="ACCEPT" onMismatch="NEUTRAL" operator="or">
<KeyValuePair key="eventId" value="Login"/>
<KeyValuePair key="eventId" value="Logout"/>
</MapFilter>
</Root>
<Appenders>
<RollingFile name="RollingFile" fileName="logs/app.log"
filePattern="logs/app-%d{MM-dd-yyyy}.log.gz">
<!-- Appender级别的Filter -->
<BurstFilter level="INFO" rate="16" maxBurst="100"/>
<PatternLayout>
<pattern>%d %p %c{1.} [%t] %m%n</pattern>
</PatternLayout>
<TimeBasedTriggeringPolicy />
</RollingFile>
</Appenders>
<Logger name="TestJavaScriptFilter" level="trace" additivity="false">
<!-- AppenderRef级别的Filter -->
<AppenderRef ref="List">
<ScriptFilter onMatch="ACCEPT" onMisMatch="DENY">
<ScriptRef ref="filter.js" />
</ScriptFilter>
</AppenderRef>
</Logger>
</Loggers>
</Configuration>
BurstFilter
Parameter Name | Type | Description |
---|---|---|
level | String | 要过滤的消息级别。 如果超过了maxBurst,则等于或低于此级别的所有内容都会被滤除。 默认值为WARN,这意味着将记录任何高于警告的消息,无论突发大小如何。 |
rate | float | 每秒允许的平均事件数。 |
maxBurst | integer | 在过滤事件超过平均速率之前可以发生的最大事件数。 默认值为速率的10倍。 |
onMatch | String | 过滤器匹配时要采取的操作。可以是ACCEPT、DENY、NEUTRAL,默认是NEUTRAL。 |
onMismatch | String | 当过滤器不匹配时要采取的操作。可以是ACCEPT、DENY、NEUTRAL,默认是DENY。 |
例如
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
<Appenders>
<RollingFile name="RollingFile" fileName="logs/app.log"
filePattern="logs/app-%d{MM-dd-yyyy}.log.gz">
<BurstFilter level="WARN" rate="10" maxBurst="2"/>
<PatternLayout>
<pattern>%d %p %c{1.} [%t] %m%n</pattern>
</PatternLayout>
<TimeBasedTriggeringPolicy />
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="RollingFile"/>
</Root>
</Loggers>
</Configuration>
输出:
2020-04-25 22:08:14,071 INFO l.TestFilter [main] test info 0
2020-04-25 22:08:14,074 WARN l.TestFilter [main] test warn 0
2020-04-25 22:08:14,074 ERROR l.TestFilter [main] test error 0
2020-04-25 22:08:14,075 ERROR l.TestFilter [main] test error 2 0
2020-04-25 22:08:14,075 ERROR l.TestFilter [main] test error 1
2020-04-25 22:08:14,075 ERROR l.TestFilter [main] test error 2 1
2020-04-25 22:08:14,075 ERROR l.TestFilter [main] test error 2
假如你把maxBurst
设置小于等于0,那么会被赋予默认值:rate*100。
CompositeFilter
复合过滤器提供了一种指定多个过滤器的方法。他以Filters
元素加入到配置中,元素里面可以配置多个过滤器。该元素不支持添加参数。
例如:
<Filters>
<MarkerFilter marker="EVENT" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
<DynamicThresholdFilter key="loginId" defaultThreshold="ERROR"
onMatch="ACCEPT" onMismatch="NEUTRAL">
<KeyValuePair key="User1" value="DEBUG"/>
</DynamicThresholdFilter>
</Filters>
DynamicThresholdFilter
DynamicThresholdFilter
允许基于特定属性按日志级别进行过滤。 例如,如果在ThreadContext Map
中捕获了用户的loginId
,则可以仅对该用户启用调试日志记录。 如果日志事件不包含指定的ThreadContext
项,则将返回NEUTRAL
。
Parameter Name | Type | Description |
---|---|---|
key | String | 去ThreadContext Map中比较的key |
defaultThreshold | String | 需要被过滤的消息等级。当指定的key不在ThreadContext中时,使用该配置。 |
keyValuePair | KeyValuePair[] | 可以定义多个KeyValuePair属性。通过该属性可以对指定用户设置日志级别。KeyValuePair的key为ThreadContext中获取的value值,KeyValuePair的value为日志的级别。 |
onMatch | String | 过滤器匹配时要采取的操作。可以是ACCEPT、DENY、NEUTRAL,默认是NEUTRAL。 |
onMismatch | String | 当过滤器不匹配时要采取的操作。可以是ACCEPT、DENY、NEUTRAL,默认是DENY。 |
例如:
public static void main(String[] args) throws InterruptedException {
ThreadContext.put("debugMode", "false");
log.info("Info should not show anywhere");
log.debug("This shouldn't show anywhere");
ThreadContext.put("debugMode", "true");
log.debug("This should show in the log and console");
log.info("This should also show in both");
ThreadContext.put("debugMode", "false");
log.info("This should not show anywhere");
log.error("This error should show only in console.");
}
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<filters>
<DynamicThresholdFilter key="debugMode" defaultThreshold="ERROR" onMatch="ACCEPT" onMismatch="DENY">
<KeyValuePair key="true" value="DEBUG"/>
<KeyValuePair key="false" value="ERROR"/>
</DynamicThresholdFilter>
</filters>
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg\n%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="TRACE" additivity="false">
<AppenderRef ref="console"/>
</Root>
</Loggers>
</Configuration>
输出:
22:54:41.416 [main] DEBUG [vsTenant] log4jstudy.TestFilter - This should show in the log and console
22:54:41.418 [main] INFO [vsTenant] log4jstudy.TestFilter - This should also show in both
22:54:41.418 [main] ERROR [vsTenant] log4jstudy.TestFilter - This error should show only in console.
ThreadContextMapFilter (or ContextMapFilter)
ThreadContextMapFilter
或ContextMapFilter
允许对当前上下文中的数据元素进行过滤。默认情况下,这是ThreadContext
映射。
Parameter Name | Type | Description |
---|---|---|
keyValuePair | KeyValuePair[] | 一个或多个KeyValuePair元素,它们定义映射中的键和要匹配的值。 如果多次指定同一键,则该键的检查将自动为“或”,因为Map只能包含一个值。KeyValuePair的key为ThreadContext中包含的key,KeyValuePair的value为ThreadContext Map中key的值。 |
operator | String | 如果运算符为“or”,则任何键/值对的匹配都将被视为匹配,否则所有键/值对必须匹配。 |
onMatch | String | 过滤器匹配时要采取的操作。可以是ACCEPT、DENY、NEUTRAL,默认是NEUTRAL。 |
onMismatch | String | 当过滤器不匹配时要采取的操作。可以是ACCEPT、DENY、NEUTRAL,默认是DENY。 |
例如:
ThreadContext.put("User1", "error name");
log.info("Info should not show anywhere");
log.debug("This shouldn't show anywhere");
ThreadContext.put("User2", "jane");
log.debug("This should show in the log and console");
log.info("This should also show in both");
ThreadContext.put("User1", "nicole");
log.info("This should show anywhere");
log.error("This error should show only in console.");
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<ContextMapFilter onMatch="ACCEPT" onMismatch="NEUTRAL" operator="or">
<KeyValuePair key="User1" value="nicole"/>
<KeyValuePair key="User2" value="jane"/>
</ContextMapFilter>
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg\n%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="WARN" additivity="false">
<AppenderRef ref="console"/>
</Root>
</Loggers>
</Configuration>
输出:
23:22:58.977 [main] DEBUG log4jstudy.TestFilter - This should show in the log and console
23:22:58.980 [main] INFO log4jstudy.TestFilter - This should also show in both
23:22:58.980 [main] INFO log4jstudy.TestFilter - This should show anywhere
23:22:58.980 [main] ERROR log4jstudy.TestFilter - This error should show only in console.
当然这个过滤器也可以被放置在logger
中:
<Loggers>
<Root level="error">
<AppenderRef ref="RollingFile"/>
<ContextMapFilter onMatch="ACCEPT" onMismatch="NEUTRAL" operator="or">
<KeyValuePair key="foo" value="bar"/>
<KeyValuePair key="User2" value="Liming"/>
</ContextMapFilter>
</Root>
</Loggers>
ThresholdFilter
如果LogEvent中的级别与配置的级别相同或更高,则此过滤器返回onMatch
结果,否则返回onMismatch
值。例如,你的日志事件为info
,但是该过滤器配置了error
,则这个事件将会被过滤掉即返回onMismatch
。
Parameter Name | Type | Description |
---|---|---|
level | String | 要匹配的有效级别名称 |
onMatch | String | 过滤器匹配时要采取的操作。可以是ACCEPT、DENY、NEUTRAL,默认是NEUTRAL。 |
onMismatch | String | 当过滤器不匹配时要采取的操作。可以是ACCEPT、DENY、NEUTRAL,默认是DENY。 |
例如:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg\n%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="INFO" additivity="false">
<AppenderRef ref="console"/>
</Root>
</Loggers>
</Configuration>
log.error("error");
log.debug("hello debug");
输出
23:27:08.024 [main] ERROR log4jstudy.TestFilter - error
小结
过滤器不仅仅有上述这些还有诸如:TimeFilter,MapFilter ,MarkerFilter ,NoMarkerFilter,RegexFilter ,ScriptFilter ,StructuredDataFilter 这些。这里只是列举了可能常用的一些过滤器,如果有需要再翻阅官方文档进行配置。