# MDC-learning
**Repository Path**: Andrew1987/mdc-learning
## Basic Information
- **Project Name**: MDC-learning
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2024-10-12
- **Last Updated**: 2024-10-12
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 什么是MDC(Mapped Diagnostic Context)
MDC(Mapped Diagnostic Context)是一个在日志框架中常用的概念,主要用于在多线程环境中关联和传递一些上下文信息,以便在日志输出中包含这些信息,从而实现更好的日志记录和调试。
在Java中,常见的日志框架如Log4j、Logback和Log4j2都提供了对MDC的支持。
MDC的主要特点包括:
线程绑定的上下文信息: MDC允许在多线程环境中将上下文信息与线程相关联。可以在应用程序的不同部分设置一些上下文信息,并确保在同一线程中的后续日志记录中能够访问到这些信息。
适用于跟踪请求或会话: MDC特别适用于跟踪请求或会话相关的信息,如请求ID、会话ID等。通过在请求开始时设置这些信息,并在请求结束时清理它们,可以确保在整个请求处理过程中,日志都包含了相同的上下文信息,方便排查问题。
日志格式化支持: MDC的值可以通过特殊的占位符在日志输出格式中引用。这样,在日志输出时,可以直接将MDC中的值包含在日志中,从而让日志更具可读性和可跟踪性。
避免参数传递的复杂性: 使用MDC可以避免在方法调用链中手动传递上下文信息的复杂性。相反,可以在适当的地方将信息设置到MDC中,在日志输出时框架会自动将这些信息包含在日志中。
简而言之,MDC是一个非常有用的工具,可以帮助开发人员在日志中记录和跟踪关键的上下文信息,提高了调试和排查问题的效率。
# Slf4j 和 MDC
**SLF4J(Simple Logging Facade for Java)**是一个日志门面框架,它提供了一种简单的方式来访问各种日志系统,例如Log4j、Logback、java.util.logging等。SLF4J本身并不是一个日志实现,而是提供了统一的接口,开发人员可以通过它来编写日志代码,而不用关心底层日志系统的具体实现。
**MDC(Mapped Diagnostic Context)**是SLF4J的一个功能,用于在日志输出中关联和传递上下文信息。MDC允许开发人员在代码中设置一些上下文信息,例如请求ID、用户ID等,然后在日志输出时将这些信息包含在日志中,以便于跟踪和调试。
SLF4J和MDC之间的关系可以总结如下:
SLF4J提供了MDC的接口: SLF4J允许开发人员通过其API来使用MDC功能。它提供了一些方法,例如MDC.put(key, value)用于设置上下文信息,MDC.get(key)用于获取上下文信息等。
MDC依赖于底层的日志实现: 虽然MDC是SLF4J提供的功能,但其实现是依赖于底层的日志实现的。不同的日志实现,如Logback、Log4j等,都有自己的MDC实现。因此,开发人员需要确保在使用MDC时,底层的日志实现已经正确配置。
MDC提供了与SLF4J日志框架的集成: MDC的设计目的之一是与SLF4J的日志框架集成得很好。这意味着开发人员可以在使用SLF4J编写的日志代码中,轻松地使用MDC功能,从而在日志中记录和跟踪上下文信息。
SLF4J和MDC是紧密相关的,MDC是SLF4J的一个功能,用于在日志输出中传递上下文信息,而SLF4J提供了使用MDC功能的接口。这使得在使用SLF4J编写的日志代码中,可以方便地利用MDC来增强日志的可读性和可追踪性。
基础工程
工程结构
```java
* MDC(Mapped Diagnostic Context)诊断上下文映射,是@Slf4j提供的一个支持动态打印日志信息的工具 */ public class TraceLogInterceptor implements HandlerInterceptor { // 追踪ID在MDC中的键名 private static final String TRACE_ID = "TRACE_ID"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 生成追踪ID,如果客户端传入了追踪ID,则使用客户端传入的追踪ID,否则使用默认的ULID生成 String tid = TraceIdGenerator.ulid(); if (StringUtils.hasLength(request.getHeader(TRACE_ID))) { tid = request.getHeader(TRACE_ID); } // 将追踪ID放入MDC中 MDC.put(TRACE_ID, tid); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) { // 请求处理完成后,从MDC中移除追踪ID MDC.remove(TRACE_ID); } } ``` 这个配置类是WebMvcConfigurer接口的实现类,用于配置拦截器。它注册了TraceLogInterceptor拦截器,并将其添加到拦截器链中。 可以通过addInterceptors方法来指定哪些请求需要被拦截,哪些请求不需要被拦截。通过这种方式,可以灵活地控制拦截器的应用范围,以满足不同的业务需求. # 方式二:自定义注解+aop ```java package com.artisan.boottrace.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author artisan */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface TraceLog { } ``` ```java package com.artisan.boottrace.aspect; import com.artisan.boottrace.utils.TraceIdGenerator; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.slf4j.MDC; import org.springframework.stereotype.Component; /** * @author 小工匠 * @version 1.0 * @mark: show me the code , change the world */ @Aspect @Component public class TraceLogAspect { private static final String TRACE_ID = "TRACE_ID"; @Before("@annotation(com.artisan.boottrace.annotations.TraceLog)") public void beforeMethodExecution() { String tid = TraceIdGenerator.ulid(); MDC.put(TRACE_ID, tid); } @After("@annotation(com.artisan.boottrace.annotations.TraceLog)") public void afterMethodExecution() { MDC.remove(TRACE_ID); } } ``` # 测试 ```java package com.artisan.boottrace.controller; import com.artisan.boottrace.service.IArtisanService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author 小工匠 * @version 1.0 * @mark: show me the code , change the world */ @Slf4j @RestController public class ArtisanTestController { @Autowired private IArtisanService artisanService; @GetMapping("/testTrace") public String testTrace(@RequestParam("name") String name) throws InterruptedException { log.info("testTrace name={}", name); doSomething1(); // 异步任务 // artisanService.addArtisan(); log.info("testTrace Call Over name={}", name); return "Hello," + name; } private void doSomething1() { log.info("doSomething1 info log"); doSomething2(); log.error("doSomething1 error log"); } private void doSomething2() { log.info("doSomething2 info log"); } } ```  ```java @TraceLog @GetMapping("/testTrace2") public String testTraceByAnno(@RequestParam("name") String name) throws InterruptedException { log.info("======================"); log.info("testTraceByAnno name={}", name); doSomething1(); log.info("testTraceByAnno Call Over name={}", name); return "Hello," + name; } ```