# 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 4.0.0 boot2 com.artisan 0.0.1-SNAPSHOT com.artisan boot-trace 0.0.1-SNAPSHOT boot-trace boot-trace 8 org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-starter-logging org.projectlombok lombok com.github.f4b6a3 ulid-creator 5.1.0 de.huxhorn.sulky de.huxhorn.sulky.ulid 8.3.0 org.springframework.boot spring-boot-maven-plugin ``` ## logback-spring.xml配置文件 ```java [%X{TRACE_ID}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}[%10method,%line] - %msg%n ${log}/%d{yyyy-MM-dd}.log 30 [%X{TRACE_ID}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 10MB ``` ### appliation.properties 配置 ```java server.port=9999 logging.config=classpath:logback-spring.xml ``` # 同步方式 方式1 拦截器 ```java package com.artisan.boottrace.interceptor; import com.artisan.boottrace.utils.TraceIdGenerator; import org.slf4j.MDC; import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author 小工匠 * @version 1.0 * @mark: show me the code , change the world *

* 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"); } } ``` ![img.png](img.png) ```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; } ```