MDC(Mapped Diagnostic Context)是SLF4J提供的一个线程安全的诊断上下文工具。它允许开发者在同一线程上下文中存储多个键值对信息,这些信息可以自动附加到日志输出中,实现日志的上下文关联。
作用 | 说明 | 典型场景 |
---|---|---|
链路追踪 | 跟踪请求完整处理流程 | 分布式系统调用跟踪 |
上下文传递 | 跨方法传递公共参数 | 用户ID、机构号等透传 |
日志增强 | 自动添加公共字段到日志 | 请求IP、设备号等记录 |
问题排查 | 快速定位特定请求的日志 | 生产环境问题诊断 |
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
dependency>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] [%X{traceId}] %-5level %logger{36} - %msg%npattern>
encoder>
appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
root>
configuration>
@Component
public class TraceInterceptor implements HandlerInterceptor {
private static final String TRACE_ID = "traceId";
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
MDC.put(TRACE_ID, UUID.randomUUID().toString());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) {
MDC.remove(TRACE_ID);
}
}
// 注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private TraceInterceptor traceInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(traceInterceptor)
.addPathPatterns("/**");
}
}
@RestController
@Slf4j
public class UserController {
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
log.info("查询用户信息,用户ID: {}", id);
// 业务逻辑...
return userService.getUser(id);
}
}
日志输出:
2023-08-20 14:30:45 [http-nio-8080-exec-1] [a1b2c3d4-e5f6-7890] INFO com.example.UserController - 查询用户信息,用户ID: 1001
public class MDCThreadPool extends ThreadPoolExecutor {
public MDCThreadPool(int corePoolSize, int maxPoolSize) {
super(corePoolSize, maxPoolSize,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(500),
new MDCThreadFactory());
}
@Override
public void execute(Runnable task) {
super.execute(wrap(task, MDC.getCopyOfContextMap()));
}
private Runnable wrap(Runnable task, Map<String, String> context) {
return () -> {
if (context != null) {
MDC.setContextMap(context);
}
try {
task.run();
} finally {
MDC.clear();
}
};
}
}
// 使用示例
private Executor executor = new MDCThreadPool(5, 10);
public void asyncProcess() {
executor.execute(() -> {
log.info("异步任务执行"); // 自动携带traceId
});
}
public class UserContext {
private static final String USER_ID = "userId";
public static void setUserId(String userId) {
MDC.put(USER_ID, userId);
}
public static String getUserId() {
return MDC.get(USER_ID);
}
public static void clear() {
MDC.remove(USER_ID);
}
}
// 在认证拦截器中设置
UserContext.setUserId(currentUser.getId());
方案 | 优势 | 适用场景 |
---|---|---|
MDC | 轻量简单、与日志框架集成好 | 单应用链路追踪 |
ThreadLocal | 更灵活的数据结构 | 复杂上下文管理 |
Sleuth | 分布式系统全链路追踪 | Spring Cloud微服务架构 |
OpenTelemetry | 云原生标准、多语言支持 | 跨语言分布式系统 |
扩展阅读:
掌握MDC的使用技巧,让您的日志系统成为排查问题的"火眼金睛"!