org.slf4j.MDC介绍-笔记

1. 功能简介

MDC (Mapped Diagnostic Context)是 SLF4J 提供的一个接口,用于在多线程环境中为每个线程存储上下文信息。这些信息通常用于日志记录,帮助开发者快速定位问题。MDC 是基于 ThreadLocal 实现的,这意味着每个线程拥有独立的上下文副本,适用于 Web 应用、异步任务等多线程场景。


主要功能

  • 存储上下文信息:通过 MDC.put(key, value) 向当前线程中放入键值对。
  • 获取上下文信息:通过 MDC.get(key) 获取当前线程中的值。
  • 清除上下文信息:通过 MDC.clear() 清除当前线程中的所有上下文信息。
  • 支持日志模板:在日志配置中通过 %X{key} 占位符引用上下文中的值。

使用场景

  • 请求跟踪:为每个请求生成唯一的 traceId,并记录到日志中,便于排查问题。
  • 用户标识:记录当前用户的 ID,便于分析用户行为。
  • 事务标识:标识某个事务的上下文,便于日志聚合。

2. 使用示例

2.1 代码演示

step1.创建一个使用MDC的过滤器

java 复制代码
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import java.io.IOException;
import java.util.UUID;


// 使用@Component,Spring Boot 会自动识别实现了 Filter 接口的 Bean,并将其注册为过滤器。
@Component
public class TraceFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化逻辑(可选)
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        // 生成唯一 traceId
        String traceId = UUID.randomUUID().toString();
        // 将 traceId 放入 MDC
        MDC.put("traceId", traceId);

        try {
            // 继续处理请求
            chain.doFilter(request, response);
        } finally {
            // 清除 MDC 上下文,避免内存泄漏
            MDC.clear();
        }
    }

    @Override
    public void destroy() {
        // 销毁逻辑(可选)
    }
}

step2.配置日志输出格式

这里以logback-spring.xml 示例:

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>custom-log-pattern: %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -traceId:%X{traceId} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

这里的 %X{traceId}日志模板的关键语法,表示:

  • 从当前线程的 MDC 中查找键为 traceId 的值;
  • 如果存在,就将这个值插入到日志中。

step3.创建测试Controller

创建个MdcTestController,用于测试 MDC 记录日志:

java 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class MdcTestController {
    private static final Logger logger = LoggerFactory.getLogger(MdcTestController.class);

    @GetMapping("MdcTestController/hello")
    public String hello() {
        logger.info("Processing request...");
        return "hello world";
    }
}

step4. 测试

浏览器发起请求: http://127.0.0.1:8080/MdcTestController/hello ,最终的日志输出如下:

java 复制代码
custom-log-pattern: 21:23:40.538 [http-nio-8080-exec-1] INFO  o.e.b.t.slf4jMdc.MdcTestController - traceId:9b4e2949-9494-48bf-83ab-bfded7919061 - Processing request...

2.2 注意事项

1. 线程池与异步处理

MDC 是基于 ThreadLocal 的,因此在使用线程池(如 Spring 的 @AsyncCompletableFuture)时,原始线程的 MDC 上下文不会自动传递到新线程中。需要手动复制上下文信息。

java 复制代码
ExecutorService executor = Executors.newFixedThreadPool(5);
Map<String, String> contextMap = MDC.getCopyOfContextMap();
executor.submit(() -> {
    if (contextMap != null) {
        MDC.setContextMap(contextMap);
    }
    try {
        // 执行任务
    } finally {
        MDC.clear();
    }
});

2. 清理上下文

务必在请求处理完成后调用 MDC.clear(),否则可能导致线程池中残留的上下文污染后续请求。

2.3 总结

MDC 是一个强大的工具,能够显著提升日志的可读性和调试效率。通过结合 Web 过滤器、日志模板和适当的清理机制,可以轻松实现请求级别的日志追踪。在处理异步任务时,需注意上下文传递的问题。

3. 相关文档

javax.servlet.Filter 介绍-笔记-CSDN博客

相关推荐
wdfk_prog1 小时前
嵌入式面试真题第 10 题:高优化等级下共享状态可见性、内存模型与系统级同步设计
java·linux·开发语言·面试·职场和发展·架构·c
卓怡学长2 小时前
w255基于springboot仓库管理系统
java·数据库·spring boot·spring·intellij-idea
这个DBA有点耶4 小时前
SQL调优进阶:从“优化一条SQL”到“优化一个系统”的思维升级
java·大数据·数据库·sql·程序人生·dba·改行学it
许彰午5 小时前
95_Python内存管理与垃圾回收
开发语言·python
csdn2015_5 小时前
springboot读取配置的方法
java·spring boot·spring
多加点辣也没关系5 小时前
JavaScript|第13章:原始类型的方法
开发语言·javascript·ecmascript
এ慕ོ冬℘゜5 小时前
深入理解 JavaScript 事件体系:Window、鼠标与键盘事件详解
开发语言·javascript·okhttp
AOwhisky5 小时前
下一代容器来了?Docker 宣布原生支持 WebAssembly
java·运维·docker·容器·rust·wasm
chouchuang7 小时前
day-030-综合练习-笔记管理器
开发语言·笔记·python
云云只是个程序马喽7 小时前
海外短剧平台搭建方案:私有化源码系统选型|云微短剧系统技术架构拆解
java·php