Springcloud学习笔记60---log4j2的MDC 原理及使用
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