190107-Log4j2之ThreadContext

Log4j2之ThreadContext

系统中使用log4j2作为日志系统,然而在高并发的情况下,多次请求的日志参杂在一起,要跟踪某个用户一次的请求操作所有日志是很麻烦的。幸运的是log4j中有相应的解决方案。

NDC和MDC

NDC(Nested Diagnostic Context)和MDC(Mapped Diagnostic Context)是log4j种非常有用的两个类,它们用于存储应用程序的上下文信息(context infomation),从而便于在log中使用这些上下文信息。NDC采用了一个类似栈的机制来push和pop上下文信息,每一个线程都独立地储存上下文信息。比如说一个servlet就可以针对每一个request创建对应的NDC,储存客户端地址等等信息。MDC和NDC非常相似,所不同的是MDC内部使用了类似map的机制来存储信息,上下文信息也是每个线程独立地储存,所不同的是信息都是以它们的key值存储在”map”中

X占位符

在官方文档的Pattern Layout章节的Patterns子章节下,可以看到对于%X占位符的描述。

描述中提到,存入ThreadContext的映射关系,能够输出到对应的X占位符中,这正好是显示登录者信息的理想实现方式。

官方文档地址:Log4j 2 API Thread Context

具体实现

  1. 修改log4j2配置文件的PatternLayout.pattern格式,追加[%X{uuid}],占位符

    1
    <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] [%X{uuid}] [%X{userId}] %-5level %logger{36} - %msg%n" />
  2. 追加Java代码,登录时,往ThreadContext存入映射关系,

    1
    2
    import org.slf4j.MDC;
    MDC.put("uuid", UUID.randomUUID().toString().replaceAll("\\-", ""));

    或者

    1
    ThreadContext.put("uuid",UUID.randomUUID().toString().replaceAll("\\-", ""));
  3. 追加Java代码,在退出登录后,清除ThreadContext的映射关系

    1
    ThreadContext.clear();

实际案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 可添加多个,这里选择拦截所有请求地址,进入后判断是否有加注解即可
registry.addInterceptor(new HandlerInterceptorAdapter() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
MDC.put("uuid", UUID.randomUUID().toString().replaceAll("\\-", ""));

if (request.getAttribute("claims") != null) {
Map<String, String> param = (Map<String, String>) request.getAttribute("claims");
MDC.put(CommonConstant.LOGIN_USER_ID, param.get(CommonConstant.LOGIN_USER_ID));
}
if (request.getHeader("user-id") != null || request.getHeader("user_id") != null) {
MDC.put(CommonConstant.LOGIN_USER_ID, StringUtils.isNotBlank(request.getHeader("user-id"))?request.getHeader("user-id"):request.getHeader("user_id"));
}
return true;
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
super.afterCompletion(request, response, handler, ex);
MDC.remove("uuid");
MDC.remove(CommonConstant.LOGIN_USER_ID);
}
}).addPathPatterns("/**");

效果

1
2
3
4
5
6
7
8
9
10
11
12
22:07:59.828 [http-nio-8012-exec-1] [bed8f0ea4df44273ba5a91e620287893] [302020072488120003] DEBUG
22:07:59.829 [http-nio-8012-exec-1] [bed8f0ea4df44273ba5a91e620287893] [302020072488120003] INFO
22:07:59.959 [http-nio-8012-exec-1] [bed8f0ea4df44273ba5a91e620287893] [302020072488120003] DEBUG
22:07:59.962 [http-nio-8012-exec-1] [bed8f0ea4df44273ba5a91e620287893] [302020072488120003] INFO
22:08:00.554 [http-nio-8012-exec-1] [bed8f0ea4df44273ba5a91e620287893] [302020072488120003] INFO
22:08:01.141 [http-nio-8012-exec-1] [bed8f0ea4df44273ba5a91e620287893] [302020072488120003] INFO
22:08:05.275 [http-nio-8012-exec-1] [bed8f0ea4df44273ba5a91e620287893] [302020072488120003] INFO
22:08:05.565 [http-nio-8012-exec-1] [bed8f0ea4df44273ba5a91e620287893] [302020072488120003] INFO
22:08:06.211 [http-nio-8012-exec-1] [bed8f0ea4df44273ba5a91e620287893] [302020072488120003] INFO
22:08:06.272 [http-nio-8012-exec-1] [bed8f0ea4df44273ba5a91e620287893] [302020072488120003] INFO
22:08:07.301 [http-nio-8012-exec-1] [bed8f0ea4df44273ba5a91e620287893] [302020072488120003] INFO
22:08:07.350 [http-nio-8012-exec-1] [bed8f0ea4df44273ba5a91e620287893] [302020072488120003] INFO

参考资料

Log4j2之ThreadContext

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×