http://www.cnblogs.com/lzb1096101803/p/5796849.html
Log4j2 简明教程
一、概述
log4j2官方文档内容非常多,要一次性了解全部是不可能的。正确的步骤应当是先了解最常见的配置,当发现原有知识无法解决问题,再重新查看文档看有没有合适的配置。
下面将从文件结构入手,再到简单的实例,从实例入手分析常见的配置的用途,其中涉及其中包括Appenders, Filters, Layout, Lookups的知识,最后根据学习。
可以搜索到的关于log4j2的教程非常少,这篇文章更多的是让大家对log4j2有个大体的了解,免得大家看到官方文档那么多就晕了!
欢迎关注我的github: https://github.com/benson-lin
如果觉得排版不好,可以访问:http://blog.bensonlin.me/post/log4j2-tutorial
log4j2.xml文件结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<?xml version=
"1.0"
encoding=
"UTF-8"
?>;
<Configuration>
<Properties>
<Property name=
"name1"
>value</property>
<Property name=
"name2"
value=
"value2"
/>
</Properties>
<Filter type=
"type"
... />
<Appenders>
<Appender type=
"type"
name=
"name"
>
<Filter type=
"type"
... />
</Appender>
...
</Appenders>
<Loggers>
<Logger name=
"name1"
>
<Filter type=
"type"
... />
</Logger>
...
<Root level=
"level"
>
<AppenderRef ref=
"name"
/>
</Root>
</Loggers>
</Configuration>
|
下面是一个比较完整的例子:
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
|
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<!-- 设置log4j2的自身log级别为warn -->
<!-- OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<configuration status=
"WARN"
monitorInterval=
"30"
>
<appenders>
<console name=
"Console"
target=
"SYSTEM_OUT"
>
<PatternLayout pattern=
"[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"
/>
</console>
<RollingFile name=
"RollingFileInfo"
fileName=
"${sys:user.home}/logs/info.log"
filePattern=
"${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log"
>
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<Filters>
<ThresholdFilter level=
"INFO"
/>
<ThresholdFilter level=
"WARN"
onMatch=
"DENY"
onMismatch=
"NEUTRAL"
/>
</Filters>
<PatternLayout pattern=
"[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"
/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size=
"100 MB"
/>
</Policies>
</RollingFile>
<RollingFile name=
"RollingFileWarn"
fileName=
"${sys:user.home}/logs/warn.log"
filePattern=
"${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log"
>
<Filters>
<ThresholdFilter level=
"WARN"
/>
<ThresholdFilter level=
"ERROR"
onMatch=
"DENY"
onMismatch=
"NEUTRAL"
/>
</Filters>
<PatternLayout pattern=
"[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"
/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size=
"100 MB"
/>
</Policies>
</RollingFile>
<RollingFile name=
"RollingFileError"
fileName=
"${sys:user.home}/logs/error.log"
filePattern=
"${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log"
>
<ThresholdFilter level=
"ERROR"
/>
<PatternLayout pattern=
"[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"
/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size=
"100 MB"
/>
</Policies>
</RollingFile>
</appenders>
<loggers>
<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
<logger name=
"org.springframework"
level=
"INFO"
></logger>
<logger name=
"org.mybatis"
level=
"INFO"
></logger>
<root level=
"all"
>
<appender-ref ref=
"Console"
/>
<appender-ref ref=
"RollingFileInfo"
/>
<appender-ref ref=
"RollingFileWarn"
/>
<appender-ref ref=
"RollingFileError"
/>
</root>
</loggers>
</configuration>
|
log4j2有默认的配置,如果要替换配置,只需要在classpath根目录下放置log4j2.xml。
log4j 2.0与以往的1.x有一个明显的不同,其配置文件只能采用.xml, .json或者 .jsn。在默认情况下,系统选择configuration文件的优先级如下:(classpath为src文件夹)
- classpath下名为 log4j-test.json 或者log4j-test.jsn文件
- classpath下名为 log4j2-test.xml
- classpath下名为 log4j.json 或者log4j.jsn文件
- classpath下名为 log4j2.xml
如果本地要测试,可以把log4j2-test.xml放到classpath,而正式环境使用log4j2.xml,则在打包部署的时候不要打包log4j2-test.xml即可。
下面是其缺省配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<Configuration status=
"WARN"
>
<Appenders>
<Console name=
"Console"
target=
"SYSTEM_OUT"
>
<PatternLayout pattern=
"%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
/>
</Console>
</Appenders>
<Loggers>
<Root level=
"error"
>
<AppenderRef ref=
"Console"
/>
</Root>
</Loggers>
</Configuration>
|
下面将对上面的配置文件进行一一讲解。
二、示例Java代码
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
|
package
com.foo;
// Import log4j classes.
import
org.apache.logging.log4j.Logger;
import
org.apache.logging.log4j.LogManager;
public
class
MyApp {
// Define a static logger variable so that it references the
// Logger instance named "MyApp".
private
static
final
Logger logger = LogManager.getLogger(MyApp.
class
);
public
static
void
main(
final
String... args) {
// Set up a simple configuration that logs on the console.
logger.trace(
"Entering application."
);
Bar bar =
new
Bar();
if
(!bar.doIt()) {
logger.error(
"Didn't do it."
);
}
logger.trace(
"Exiting application."
);
}
}
package
com.foo;
import
org.apache.logging.log4j.LogManager;
import
org.apache.logging.log4j.Logger;
public
class
Bar {
static
final
Logger logger = LogManager.getLogger(Bar.
class
.getName());
public
boolean
doIt() {
logger.entry();
logger.error(
"Did it again!"
);
return
logger.exit(
false
);
}
}
|
如果使用如下配置,也就是缺省配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<Configuration status=
"WARN"
>
<Appenders>
<Console name=
"Console"
target=
"SYSTEM_OUT"
>
<PatternLayout pattern=
"%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
/>
</Console>
</Appenders>
<Loggers>
<Root level=
"error"
>
<AppenderRef ref=
"Console"
/>
</Root>
</Loggers>
</Configuration>
|
输出如下:只输出error以上的日志信息
1
2
|
17
:
13
:
01.540
[main] ERROR com.foo.Bar - Did it again!
17
:
13
:
01.540
[main] ERROR MyApp - Didn't
do
it.
|
如果我们希望除了com.foo.Bar类下输出TRACE以上到控制台外,其他停止TRACE的输出到控制台,只输出ERROR以上的日志。可以如下配置:
1
2
3
4
5
6
|
<Loggers>
<Logger name=
"com.foo.Bar"
level=
"TRACE"
/>
<Root level=
"ERROR"
>
<AppenderRef ref=
"STDOUT"
>
</Root>
</Loggers>
|
结果如下:
1
2
3
4
|
14
:
14
:
17.176
[main] TRACE com.foo.Bar - Enter
14
:
14
:
17.182
[main] ERROR com.foo.Bar - Did it again!
14
:
14
:
17.182
[main] TRACE com.foo.Bar - Exit with(
false
)
14
:
14
:
17.182
[main] ERROR com.foo.MyApp - Didn't
do
it.
|
因为com.foo.Bar没有自己的Appender,所以会使用ROOT的Appender,如果自己也配置了在控制台打印,就要注意可加性:如下配置,会ERROR以上的会打印两次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<Configuration status=
"WARN"
>
<Appenders>
<Console name=
"Console"
target=
"SYSTEM_OUT"
>
<PatternLayout pattern=
"%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
/>
</Console>
</Appenders>
<Loggers>
<Logger name=
"com.foo.Bar"
level=
"trace"
>
<AppenderRef ref=
"Console"
/>
</Logger>
<Root level=
"error"
>
<AppenderRef ref=
"Console"
/>
</Root>
</Loggers>
</Configuration>
|
结果如下
1
2
3
4
5
6
7
|
14
:
11
:
27.103
[main] TRACE com.foo.Bar - Enter
14
:
11
:
27.103
[main] TRACE com.foo.Bar - Enter
14
:
11
:
27.106
[main] ERROR com.foo.Bar - Did it again!
14
:
11
:
27.106
[main] ERROR com.foo.Bar - Did it again!
14
:
11
:
27.107
[main] TRACE com.foo.Bar - Exit with(
false
)
14
:
11
:
27.107
[main] TRACE com.foo.Bar - Exit with(
false
)
14
:
11
:
27.107
[main] ERROR com.foo.MyApp - Didn't
do
it.
|
如果我们确实有这种需求(不想遵循父类的Appender),可以加上additivity="false"
参数。如下配置,com.foo.Bar的trace以上日志将保存到文件中,并且不会打印到控制台。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<Configuration status=
"WARN"
>
<Appenders>
<Console name=
"Console"
target=
"SYSTEM_OUT"
>
<PatternLayout pattern=
"%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
/>
</Console>
<RollingFile name=
"RollingFile"
fileName=
"${sys:user.home}/logs/trace.log"
filePattern=
"${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log"
>
...
</RollingFile>
</Appenders>
<Loggers>
<Logger name=
"com.foo.Bar"
level=
"trace"
additivity=
"false"
>
<AppenderRef ref=
"RollingFile"
/>
</Logger>
<Root level=
"error"
>
<AppenderRef ref=
"Console"
/>
</Root>
</Loggers>
</Configuration>
|
log4j2支持自动重新配置,如果配置了monitorInterval,那么log4j2每隔一段时间就会检查一遍这个文件是否修改。最小是5s
1
2
3
4
|
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<Configuration monitorInterval=
"30"
>
...
</Configuration>
|
三、Appenders
ConsoleAppender
将使用 System.out 或 System.err输出到控制台。
可以有如下参数
- name:Appender的名字
- target:SYSTEM_OUT 或 SYSTEM_ERR,默认是SYSTEM_OUT
- layout:如何格式化,如果没有默认是%m%n
典型的ConsoleAppender如下
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<Configuration status=
"warn"
name=
"MyApp"
packages=
""
>
<Appenders>
<Console name=
"STDOUT"
target=
"SYSTEM_OUT"
>
<PatternLayout pattern=
"%m%n"
/>
</Console>
</Appenders>
<Loggers>
<Root level=
"error"
>
<AppenderRef ref=
"STDOUT"
/>
</Root>
</Loggers>
</Configuration>
|
RollingFileAppender
顾名思义,日志文件回滚,也就是删除最旧的日志文件,默认是3个文件。可以通过DefaultRolloverStrategy设置max参数为多个
例子如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<Appenders>
<RollingFile name=
"RollingFile"
fileName=
"logs/app.log"
filePattern=
"logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"
>
<PatternLayout>
<Pattern>%d %p %c{
1
.} [%t] %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size=
"250 MB"
/>
</Policies>
<DefaultRolloverStrategy max=
"20"
/>
</RollingFile>
</Appenders>
|
现在说说TimeBasedTriggeringPolicy和SizeBasedTriggeringPolicy的作用。
第一个是基于时间的rollover,第二个是基于大小的rollover。第二个很容易理解,如果大小大于某个阈值,上面是50MB的时候就会滚动。
TimeBasedTriggeringPolicy中有其中一个参数是interval,表示多久滚动一次。默认是1 hour。modulate=true用来调整时间:比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am,12am...而不是7am
四、Layouts
这里只描述最常见的PatternLayout!更多看官方文档Layouts
1
2
3
4
5
6
7
8
9
10
|
<RollingFile name=
"RollingFileError"
fileName=
"${sys:user.home}/logs/error.log"
filePattern=
"${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log"
>
<ThresholdFilter level=
"ERROR"
/>
<PatternLayout pattern=
"[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"
/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size=
"50 MB"
/>
</Policies>
<DefaultRolloverStrategy max=
"20"
/>
</RollingFile>
|
上面的%是什么含义,还有哪些呢?其实最主要的参数还是%d, %p, %l, %m, %n, %X。下面的图是摘取网上的。
%X用来获取MDC记录,这些记录从哪来的?我们可以使用org.apache.logging.log4j.ThreadContext将需要记录的值put进去。(我发现slf的MDC.java的put方法对log4j2不可用,因为底层依赖的是log4j1)
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
|
package
com.bensonlin.service.web.interceptor;
import
javax.servlet.http.HttpServletRequest;
import
javax.servlet.http.HttpServletResponse;
import
org.apache.logging.log4j.ThreadContext;
import
org.springframework.web.servlet.HandlerInterceptor;
import
org.springframework.web.servlet.ModelAndView;
public
class
MDCInterceptor
implements
HandlerInterceptor {
public
final
static
String USER_KEY =
"user_id"
;
public
final
static
String REQUEST_REQUEST_URI =
"request_uri"
;
public
boolean
preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object arg2)
throws
Exception {
ThreadContext.put(REQUEST_REQUEST_URI, httpServletRequest.getRequestURI());
return
true
;
}
public
void
postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object arg2,
ModelAndView modelAndView)
throws
Exception {
}
public
void
afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
Object arg2, Exception exception)
throws
Exception {
ThreadContext.remove(USER_KEY);
ThreadContext.remove(REQUEST_REQUEST_URI);
}
public
static
void
setUserKeyForMDC(String userId) {
ThreadContext.put(USER_KEY, userId);
}
}
|
xml中使用%X{aaa}取出来:
1
2
3
|
<console name=
"Console"
target=
"SYSTEM_OUT"
>
<PatternLayout pattern=
"%X{user_id} %X{request_uri} [%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"
/>
</console>
|
对应ThreadContext的文档在这里
五、Filters
Filters决定日志事件能否被输出。过滤条件有三个值:ACCEPT(接受), DENY(拒绝) or NEUTRAL(中立).
ACCEP和DENY比较好理解就是接受和拒绝的意思,在使用单个过滤器的时候,一般就是使用这两个值。但是在组合过滤器中,如果用接受ACCEPT的话,日志信息就会直接写入日志文件,后续的过滤器不再进行过滤。所以,在组合过滤器中,接受使用NEUTRAL(中立),被第一个过滤器接受的日志信息,会继续用后面的过滤器进行过滤,只有符合所有过滤器条件的日志信息,才会被最终写入日志文件。
ThresholdFilter
有几个参数:
- level:将被过滤的级别。
- onMatch:默认值是NEUTRAL
- onMismatch:默认是DENY
如果LogEvent 中的 Log Level 大于 ThresholdFilter 中配置的 Log Level,那么返回 onMatch 的值, 否则返回 onMismatch 的值,例如 : 如果ThresholdFilter 配置的 Log Level 是 ERROR , LogEvent 的Log Level 是 DEBUG。 那么 onMismatch 的值将被返回, 因为 ERROR 小于DEBUG。如果是Accept,将自己被接受,而不经过下一个过滤器
下面的例子可以这样理解:如果是INFO级别及其以上,将经过通过第一个过滤,进入第二个,否则是onMismatch:拒绝进入。对于第二个,如果是大于等于WARN(WARN/ERROR/ERROR),那么将返回onMatch,也就是拒绝,如果是其他情况(也就是INFO),将是中立情况,因为后面没有其他过滤器,则被接受。最后的结果就只剩下INFO级别的日志。也就符合了RollingFileInfo只记录Info级别的规则。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<RollingFile name=
"RollingFileInfo"
fileName=
"${sys:user.home}/logs/info.log"
filePattern=
"${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log"
>
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<Filters>
<ThresholdFilter level=
"INFO"
/>
<ThresholdFilter level=
"WARN"
onMatch=
"DENY"
onMismatch=
"NEUTRAL"
/>
</Filters>
<PatternLayout pattern=
"[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"
/>
<Policies>
<TimeBasedTriggeringPolicy interval=
"24"
modulate=
"true"
/>
<SizeBasedTriggeringPolicy size=
"50 MB"
/>
</Policies>
<DefaultRolloverStrategy max=
"20"
/>
</RollingFile>
|
六、Lookups
提供另外一种方式添加某些特殊的值到日志中。
Date Lookup
与其他lookup不同,它不是通过key去查找值,而是通过SimpleDateFormat验证格式是否有效,然后记录当前时间
1
2
3
4
5
6
|
<RollingFile name=
"Rolling-${map:type}"
fileName=
"${filename}"
filePattern=
"target/rolling1/test1-$${date:MM-dd-yyyy}.%i.log.gz"
>
<PatternLayout>
<pattern>%d %p %c{
1
.} [%t] %m%n</pattern>
</PatternLayout>
<SizeBasedTriggeringPolicy size=
"500"
/>
</RollingFile>
|
Context Map Lookup: 如记录loginId
1
2
3
4
5
|
<File name=
"Application"
fileName=
"application.log"
>
<PatternLayout>
<pattern>%d %p %c{
1
.} [%t] $${ctx:loginId} %m%n</pattern>
</PatternLayout>
</File>
|
这个的结果和前面的MDC是一样的,即 %X{loginId}
Environment Lookup:记录系统环境变量
比如可以获取如/etc/profile
中的变量值
1
2
3
4
5
|
<File name=
"Application"
fileName=
"application.log"
>
<PatternLayout>
<pattern>%d %p %c{
1
.} [%t] $${env:USER} %m%n</pattern>
</PatternLayout>
</File>
|
System Properties Lookup
可以获取Java中的系统属性值。
1
2
3
|
<Appenders>
<File name=
"ApplicationLog"
fileName=
"${sys:logPath}/app.log"
/>
</Appenders>
|
和系统属性值有什么区别呢?其实就是System.getProperties();和System.getenv();的区别。下面是一个小例子。
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
|
package
com.bensonlin.service.common;
import
java.util.Iterator;
import
java.util.Map;
import
java.util.Map.Entry;
import
java.util.Properties;
public
class
Main {
public
static
void
main(String[] args) {
Properties properties = System.getProperties();
Iterator i = properties.entrySet().iterator();
while
(i.hasNext()) {
Map.Entry entry = (Map.Entry) i.next();
Object key = entry.getKey();
Object value = entry.getValue();
System.out.println(key +
"="
+ value);
}
System.out.println(
"==================="
);
Map map = System.getenv();
Iterator it = map.entrySet().iterator();
while
(it.hasNext()) {
Entry entry = (Entry) it.next();
System.out.print(entry.getKey() +
"="
);
System.out.println(entry.getValue());
}
}
}
|
输出(摘取部分):
1
2
3
4
5
6
7
8
9
10
11
|
java.runtime.name=Java(TM) SE Runtime Environment
sun.boot.library.path=C:\Program Files\Java\jdk1.
8
.0_25\jre\bin
java.vm.version=
25.25
-b02
java.vm.vendor=Oracle Corporation
java.vendor.url=http:
//java.oracle.com/
path.separator=;
...
===================
JAVA_HOME=C:\Program Files\Java\jdk1.
8
.0_25
TEMP=D:\Temp
ProgramFiles=C:\Program Files
|
...
可以看到其实Environment是获取环境变量,而System Properties获取的更多是与Java相关的值
转载注明地址:http://blog.bensonlin.me/post/log4j2-tutorial