Log:log4j2.xml 配置示例和异步日志详解
Log4j2配置:log4j2.xml
文件示例配置; Log4j2 官方文档
异步日志
log4j2 支持异步日志功能, 可以大幅提高日志记录的性能。AsyncAppender,Async Loggers
AsyncAppender
AsyncAppender 默认使用 java.util.concurrent.ArrayBlockingQueue jdk内部库, 这是一个阻塞队列, 容易受锁争用的影响,在多线程环境时性能会降低变差,建议使用无锁异步记录器来获得最佳性能。
1 |
|
Async Loggers
LMAX Disruptor技术。异步记录器在内部使用Disruptor,一个无锁的线程间通信库,而不是队列,从而产生更高的吞吐量和更低的延迟,异步记录器的延迟始终低于同步记录器甚至基于队列的 AsyncAppender。Disruptor技术支持全异步,混合异步, 性能优于AsyncAppender。
如果记录的日志来自于业务逻辑, 官方建议使用同步记录日志消息,是因为 Async Loggers 在记录日志过程中发生问题并抛出异常,存在不容易通知到应用程序的情况。
使用 Async Loggers 异步记录日志,需要添加 disruptor 依赖,Log4j-2.9及更高版本在类路径上需要 disruptor-3.3.4.jar 或更高版本。 在Log4j-2.9之前,需要disruptor-3.0.0.jar或更高版本。
默认情况下,异步记录器不会将位置传递给 I/O 线程。 如果您的某个布局或自定义过滤器需要位置信息,则需要在所有相关记录器的配置中设置includeLocation = true
,包括根记录器。
- 全异步记录日志
需要设置系统环境变量:Don’t forget to set system property-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
to make all loggers asynchronous.当使用 AsyncLoggerContextSelector 使所有记录器异步时,请确保在配置中使用普通的1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- Don't forget to set system property
-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
to make all loggers asynchronous. -->
<Configuration status="WARN">
<Appenders>
<!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. -->
<RandomAccessFile name="RandomAccessFile" fileName="async.log" immediateFlush="false" append="false">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m %ex%n</Pattern>
</PatternLayout>
</RandomAccessFile>
</Appenders>
<Loggers>
<Root level="info" includeLocation="false">
<AppenderRef ref="RandomAccessFile"/>
</Root>
</Loggers>
</Configuration><root> 和 <logger>
元素。 - 混合异步
混合异步:无需设置系统环境变量 Log4jContextSelector。
使用<asyncRoot>
或<asyncLogger>
配置元素指定需要异步的记录器。配置只能包含一个根记录器(<root>或<asyncRoot>
元素),但是可以组合异步和非异步记录器。例如,包含<asyncLogger>
元素的配置文件还可以包含同步记录器的<root> 和 <logger>
元素。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
<!-- No need to set system property "log4j2.contextSelector" to any value
when using <asyncLogger> or <asyncRoot>. -->
<Configuration status="WARN">
<Appenders>
<!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. -->
<RandomAccessFile name="RandomAccessFile" fileName="asyncWithLocation.log"
immediateFlush="false" append="false">
<PatternLayout>
<Pattern>%d %p %class{1.} [%t] %location %m %ex%n</Pattern>
</PatternLayout>
</RandomAccessFile>
</Appenders>
<Loggers>
<!-- pattern layout actually uses location, so we need to include it -->
<AsyncLogger name="com.foo.Bar" level="trace" includeLocation="true">
<AppenderRef ref="RandomAccessFile"/>
</AsyncLogger>
<Root level="info" includeLocation="true">
<AppenderRef ref="RandomAccessFile"/>
</Root>
</Loggers>
</Configuration>
同步日志
没有配置异步日志,默认就是同步日志。
log4j2.xml 配置示例
1 |
|
LogId
查看日志时,有时需要查看一个线程的整个日志流来分析整个处理流程,这时可以配置 LogId 唯一标识来标记同一个请求。
Log4j2 提供了提供了个 Thread Context(线程上下文)可以对线程进行日志标记,通过该日志标记可以记录线程的处理流程。
- 在日志输出模式添加LogId
1
<Property name="log.pattern">%d %p [LogId:%X{LogId}] [%C{1.}->%M:%L] [%t] %m%n</Property>
- 给每个请求创建全局唯一LogId,非常方便根据LogId链跟踪整个请求的处理过程
监听器方式,建议使用此方式也可使用拦截器实现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
27import org.apache.logging.log4j.ThreadContext;
import org.springframework.stereotype.Component;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
/**
* @name: RequestListener
* @desc: 给请求增加全局唯一ID给日志输出用
**/
public class RequestListener implements ServletRequestListener {
public void requestInitialized(ServletRequestEvent sre) {
//给请求增加全局唯一ID给日志输出用
long logId = System.currentTimeMillis();
ThreadContext.put("LogId", String.valueOf(logId));
}
public void requestDestroyed(ServletRequestEvent sre) {
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//拦截器
public class LogIdInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
long logId = System.currentTimeMillis();
ThreadContext.put("LogId",String.valueOf(logId));
return true;
}
}
//WebConfig
public class WebAppConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogIdInterceptor());
}
} - 输出日志效果
1
2
3
42018-11-09 18:15:08,673 INFO [LogId:1541758508672] [c.s.t.c.ShiroRealm->doGetAuthenticationInfo:34] [http-nio-8080-exec-6] 开始认证用户身份 .....
2018-11-09 18:15:08,674 INFO [LogId:1541758508672] [n.s.l.l.s.Slf4jSpyLogDelegator->sqlOccurred:228] [http-nio-8080-exec-6] SELECT count(0) FROM user WHERE username = 'user1'
2018-11-09 18:15:08,675 INFO [LogId:1541758508672] [n.s.l.l.s.Slf4jSpyLogDelegator->resultSetCollected:610] [http-nio-8080-exec-6]
参数说明
- %m 输出代码中指定的消息
- %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
- %r 输出自应用启动到输出该log信息耗费的毫秒数
- %c 输出所属的类目,通常就是所在类的全名
- %t 输出产生该日志事件的线程名
- %n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”
- %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 :10:28,921
- %l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数
- %x Used to output the NDC (nested diagnostic context) associated with the thread that generated the logging event
- %X Used to output the MDC (mapped diagnostic context) associated with the thread that generated the logging event for specified key
相关参考
Log:log4j2.xml 配置示例和异步日志详解
http://blog.gxitsky.com/2018/03/31/Log-log4j2-setting-sync-logger/