最近遇到一个问题,使用slf4j和log4j2日志框架时,需要将请求的id放入到打印日志中。因为MDC和ThreadContext是跟线程绑定的,所以遇到了子线程无法读取父线程MDC和ThreadContext的问题,网上搜了很多,有各种各样的答案,折腾了一天,总算搞定了,今天花点时间把答案总结梳理一下,希望大家能少走弯路。

问题现象:在父线程中使用了MDC.put("key","value")或者ThreadContext.put("key","value"),在子线程中使用MDC.get("key")或ThreadContext.get("key")或使用%X{key}打印日志都无法读取到对应值。

方法一:最简单的方法但是最容易踩坑。

使用MDC.getCopyOfContextMap()和MDC.setContextMap(MAP<String,String>),根据方法名我想当然以为在父线程中setContextMap,在子线程中getCopyOfContextMap,然而并不是这样!!!下面是正确的写法。

 

for 子线程 线程执行完 执行主线程 java 子线程获取父线程_开发语言

应该是先在父线程getCopyOfContextMap,再在子线程setContextMap。翻一下源码,其实也能理解,要先生成了map对象,然后里面存放了父线程的数据,在子线程中把它放到MDC中,那么子线程就可以读取其中的数据了,完美。

方法二:改系统变量,又是一个大坑。

我本地的项目是一个java项目,只有一个类,然后我按照网上的办法初始化了log4j2.isThreadContextMapInheritable为true。代码如下

for 子线程 线程执行完 执行主线程 java 子线程获取父线程_log4j2_02

 这个很简单就成功了,但是,换到开发环境上的web项目,就死活不行了。经过分析发现,必须在Logger对象第一次初始化之前,就要把环境变量设置进去。。。

然后就去tomcat的配置文件里面搞了,在catalina,.sh或catalina.bat里面,给JAVA_OPTS加上-Dlog4j2.isThreadContextMapInheritable=true。重启应用完活。

方法三:昨天读了一下午源码,也找到了,但是脑子就是没反应过来,气死了。

在resources目录下,增加log4j2.component.properties文件,在里面写上isThreadContextMapInheritable=true,这样log4j2会自己去读取配置文件,就不用再改其他地方了,这个在源码里面可以找到。

这三种办法都亲测可行,但是昨天在网上找资料,都写的比较简单,在这总结一下,希望能对大家有帮忙把。