Springcloud学习笔记60---log4j2的MDC 原理及使用

雨后观山色 / 2024-01-19 / 原文

1. 使用背景

slf4j是门面,log4j2是一种具体的实现。我们先看官网 slf4j的官网SLF4J 全称 Simple Logging Facade for Java 。主要是给java日志访问提供了一个标准,规范的API接口。具体实现由不同的日志框架实现,比如log4j2,logback。

我们项目中使用的log4j2日志框架,在日志输出的时候,我们有个需求,需要将我们全局唯一的流程流水id打印到log4j的日志文件中。

2. MDC 基础概念及原理

MDC ( Mapped Diagnostic Contexts ),顾名思义,其目的是为了便于我们诊断线上问题而出现的方法工具类。虽然,Slf4j 是用来适配其他的日志具体实现包的,但是针对 MDC功能,目前只有logback 以及 log4j 支持。

先来看看 MDC 对外提供的接口:

public class MDC {
  //Put a context value as identified by key
  //into the current thread's context map.
  public static void put(String key, String val);

  //Get the context identified by the key parameter.
  public static String get(String key);

  //Remove the context identified by the key parameter.
  public static void remove(String key);

  //Clear all entries in the MDC.
  public static void clear();
}

以put方法为入口;

    public static void put(String key, String val) throws IllegalArgumentException {
        if (key == null) {
            throw new IllegalArgumentException("key parameter cannot be null");
        }
        if (mdcAdapter == null) {
            throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
        }
        mdcAdapter.put(key, val);
    }
public class Log4jMDCAdapter implements MDCAdapter {

    @Override
    public void put(final String key, final String val) {
        ThreadContext.put(key, val);
    }
    .....
}

最终进入CopyOnWriteSortedArrayThreadContextMap中;

class CopyOnWriteSortedArrayThreadContextMap implements ReadOnlyThreadContextMap, ObjectThreadContextMap, CopyOnWrite {
    .......

    private final ThreadLocal<StringMap> localMap;
    //构造方法
    public CopyOnWriteSortedArrayThreadContextMap() {
        this.localMap = createThreadLocalMap();
    }

    // LOG4J2-479: by default, use a plain ThreadLocal, only use InheritableThreadLocal if configured.
    // (This method is package protected for JUnit tests.)
    private ThreadLocal<StringMap> createThreadLocalMap() {
        if (inheritableMap) {
            return new InheritableThreadLocal<StringMap>() {
                @Override
                protected StringMap childValue(final StringMap parentValue) {
                    if (parentValue == null) {
                        return null;
                    }
                    final StringMap stringMap = createStringMap(parentValue);
                    stringMap.freeze();
                    return stringMap;
                }
            };
        }
        // if not inheritable, return plain ThreadLocal with null as initial value
        return new ThreadLocal<>();
    }
    ......
}
    @Override
    public void put(final String key, final String value) {
        putValue(key, value);
    }

    @Override
    public void putValue(final String key, final Object value) {
        StringMap map = localMap.get();
        map = map == null ? createStringMap() : createStringMap(map);
        map.putValue(key, value);
        map.freeze();
        localMap.set(map);
    }

到此,我们可以看到MDC底层用的是ThreadLocal。

主要说明了两点:   

    MDC 主要用于保存上下文,区分不同的请求来源。
    MDC 管理是按线程划分,并且子线程会自动继承母线程的上下文。

InheritableThreadLocal 说明:该类扩展了 ThreadLocal,为子线程提供从父线程那里继承的值:在创建子线程时,子线程会接收所有可继承的线程局部变量的初始值,以获得父线程所具有的值。通常,子线程的值与父线程的值是一致的;但是,通过重写这个类中的 childValue 方法,子线程的值可以作为父线程值的一个任意函数。

 

参考文献:

https://blog.csdn.net/f80407515/article/details/119239021

https://blog.csdn.net/m0_37556444/article/details/100142429