MDC

获取父线程MDC中的内容 Map<String, String> context = MDC.getCopyOfContextMap();

设置contentMap MDC.setContextMap(context);

清空MDC内容 `MDC.clear();

实现线程池传递MDC
*{@link ThreadPoolTaskExecutor}的一个简单替换,可以在每个任务之前设置子线程的MDC数据。

  • 在记录日志的时候,一般情况下我们会使用MDC来存储每个线程的特有参数,如身份信息等,以便更好的查询日志。
  • 但是Logback在最新的版本中因为性能问题,不会自动的将MDC的内存传给子线程。所以Logback建议在执行异步线程前
  • 先通过MDC.getCopyOfContextMap()方法将MDC内存获取出来,再传给线程。
  • 并在子线程的执行的最开始调用MDC.setContextMap(context)方法将父线程的MDC内容传给子线程。
  • https://logback.qos.ch/manual/mdc.html
    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
    public class MdcThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {

    /**
    * 所有线程都会委托给这个execute方法,在这个方法中我们把父线程的MDC内容赋值给子线程
    * https://logback.qos.ch/manual/mdc.html#managedThreads
    *
    * @param runnable
    */
    @Override
    public void execute(Runnable runnable) {
    // 获取父线程MDC中的内容,必须在run方法之前,否则等异步线程执行的时候有可能MDC里面的值已经被清空了,这个时候就会返回null
    Map<String, String> context = MDC.getCopyOfContextMap();
    super.execute(() -> run(runnable, context));
    }

    /**
    * 子线程委托的执行方法
    *
    * @param runnable {@link Runnable}
    * @param context 父线程MDC内容
    */
    private void run(Runnable runnable, Map<String, String> context) {
    // 将父线程的MDC内容传给子线程
    if (context != null) {
    try {
    MDC.setContextMap(context);
    } catch (Exception e) {
    logger.error(e.getMessage(), e);
    }
    }
    try {
    // 执行异步操作
    runnable.run();
    } finally {
    // 清空MDC内容
    MDC.clear();
    }
    }
    }