Log4j的MDC机制如何在线程池中使用

问题:我们希望 当有一个请求时, 全部相应后台日志增加一个统一的全局requestId 方便日志搜集和处理:

我们可以使用sl4j MDC机制。

问题:sl4j MDC机制原理:

通常用于保存线程本地的“诊断数据”然后有日志组件打印,其内部时基于threadLocal实现

问题:MDC使用:

步骤 1 在拦截器 或者 统一的方法入口加上

MDC.put(“REQUEST_ID”,xxx);

步骤 2 改动log4j日志格式,增加该字段 REQUEST_ID 比如下面例子即可:

改前:%-d{yyyy-MM-dd HH:mm:ss,SSS} [%p] % %m%n
改后:%-d{yyyy-MM-dd HH:mm:ss,SSS} [%p] %X{REQUEST_ID} %m%n 

问题:但对于多线程情况,比如主线程创建了一些子线程,那么这些子线程能不能自动带上MDC所赋的值呢?

答案是可能会出问题。

我们看官方文档说明:https://logging.apache.org/log4j/2.x/manual/thread-context.html

=========================================================================================

Implementation details
The Stack and the Map are managed per thread and are based on ThreadLocal by default. The Map can be configured to use an InheritableThreadLocal by setting system property log4j2.isThreadContextMapInheritable to true. When configured this way, the contents of the Map will be passed to child threads. However, as discussed in the Executors class and in other cases where thread pooling is utilized, the ThreadContext may not always be automatically passed to worker threads. In those cases the pooling mechanism should provide a means for doing so. The getContext() and cloneStack() methods can be used to obtain copies of the Map and Stack respectively.

Note that all methods of the ThreadContext class are static.

翻译过来 ==================================================================================

实施细节
堆栈和映射是按线程管理的,默认情况下基于ThreadLocal。 通过将系统属性log4j2.isThreadContextMapInheritable设置为true,可以将Map配置为使用InheritableThreadLocal。 通过这种方式配置后,映射的内容将传递给子线程。 但是,如在Executors类中讨论的以及在其他使用线程池的情况下,ThreadContext可能不会始终自动传递给工作线程。 在这些情况下,池化机制应提供这样做的手段。 getContext()和cloneStack()方法可分别用于获取Map和Stack的副本。

请注意,ThreadContext类的所有方法都是静态的。

 =========================================================================================

测试使用的是线程池,跑了一次 发现线程池中的子线程会把MDC的值打印出来,并且和主线程MDC值一致,与官方文档矛盾,经过分析发现:

用线程池的时候会有问题的,测试ok,是因为工作线程第一次的时候是当前的线程创建的,如果复用的话会出现问题的,果然把线程池大小改成1,

然后多调几次就发现子线程中的MDC值永远是第一次创建时的值不会变,

所以确认官方文档是对的:主线程中设置的MDC数据,在其子线程(多线程池)中是无法获取的。

下面就来介绍如何解决这个问题。

 在子线程类经过如上代码改动,则主线程中的MDC值会成功的自动传递给该子线程,亲测有效。

猜你喜欢

转载自www.cnblogs.com/wanghongsen/p/12029394.html