相关文章推荐
坚韧的松鼠  ·  Spring Shell ...·  21 小时前    · 
茫然的小虾米  ·  Python ...·  2 月前    · 
腹黑的打火机  ·  qml 图片 image ...·  1 年前    · 

MDC介绍
1、简介:

MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 、logback及log4j2 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。

2、一般时候,我们为了日志追踪,会在日志中拼接参数,代码具有侵入性,也容易忘记。MDC结合log4j,程序会在适当时候当我们拼接好参数。

MDC使用
1.拦截器中使用MDC put 键值对。使得所有请求,从一开始就标记上特定标识。
2.如果是微服务之间的调用,则需要上层服务在header中添加标识同请求一起传输过来。下层服务直接使用上层服务的标识,就可以将日志串联起来。

public class LogInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //如果有上层调用就用上层的ID
        String traceId = request.getHeader(Constants.TRACE_ID);
        if (traceId == null) {
            traceId = UUID.randomUUID().toString();
        MDC.put(Constants.TRACE_ID, traceId);
        return true;
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        MDC.clear();

重点:修改logback日志格式

<property name="pattern">[TRACEID:%X{traceId}] %d{HH:mm:ss.SSS} %-5level %class{-1}.%M()/%L - %msg%xEx%n</property>

也可以只在配置文件中添加配置

logging.pattern.console="%d %.-1p traceId:[%X{traceId}] --- [%t] %logger{16} : %replace(%m%wEx){'[\r\n]+', '\t'}%nopex%n"

MDC的坑
1.主线程中,如果使用了线程池,会导致线程池中丢失MDC信息;
解决办法:需要我们自己重写线程池,在调用线程跳动run之前,获取到主线程的MDC信息,重新put到子线程中的。

public class MDCLogThreadPoolExecutor extends ThreadPoolExecutor {
    public MDCLogThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    @Override
    public void execute(Runnable command) {
        super.execute(MDCLogThreadPoolExecutor.executeRunable(command, MDC.getCopyOfContextMap()));
    @Override
    public Future<?> submit(Runnable task) {
        return super.submit(MDCLogThreadPoolExecutor.executeRunable(task, MDC.getCopyOfContextMap()));
    @Override
    public <T> Future<T> submit(Callable<T> callable) {
        return super.submit(MDCLogThreadPoolExecutor.submitCallable(callable,MDC.getCopyOfContextMap()));
    public static Runnable executeRunable(Runnable runnable ,Map<String,String> mdcContext){
        return new Runnable() {
            @Override
            public void run() {
                if (mdcContext == null) {
                    MDC.clear();
                } else {
                    MDC.setContextMap(mdcContext);
                try {
                    runnable.run();
                } finally {
                    MDC.clear();
    private static <T> Callable<T> submitCallable( Callable<T> callable,  Map<String, String> context) {
        return () -> {
            if (context == null) {
                MDC.clear();
            } else {
                MDC.setContextMap(context);
            try {
                return callable.call();
            } finally {
                MDC.clear();
            <groupId>com.glzt</groupId>
            <artifactId>feignextend</artifactId>
            <version>1.0.0-SNAPSHOT</version>
 </dependency>
2.基于logback、log4j的MDC机制
3.日志配置中添加traceId引用,如下:
<Pattern>[%date{yyyy-MM-dd HH:mm:ss.SSS}] [%X{traceId}] [%thread] %-5level %logger{80} %line - %msg%n</Pattern>
					
解释一下java代码Map mdcContext = MDC.getCopyOfContextMap();MDC.setContextMap(mdcContext);是什么意思?
一、什么是MDC? MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能,也可以说是一种轻量级的日志跟踪工具。 二、MDC能为我们做什么? 当我们在开发过程中遇到问题时,我们大多数情况下都会借助查询日志寻找问题,那么如何在海量的日志中查询到我们某个请求的一系列调用信息便显得尤为重要,那么MDC就能为我们解决这一问题。 三、MDC衍生的具体场景 在开发过程中,大多数情况下我们的一个请求调用链路可能相当复杂,并
* This class hides and serves as a substitute for the underlying logging * system's MDC implementation. * If the underlying logging system offers MDC functionality,