MDC 实现 traceId 记录

MDC 实现 traceId 记录

目的

  • 在web请求中记录实现 traceId追踪
  • 能线程池中也可以传递 traceId

通用

ThreadPoolTaskExecutor 实现 MDC 传递
public class MdcTaskExecutorCustomizer implements TaskExecutorCustomizer {

    @Override
    public void customize(ThreadPoolTaskExecutor taskExecutor) {
        taskExecutor.setTaskDecorator(runnable -> {
            Map<String, String> context = MDC.getCopyOfContextMap();
            return () -> {
                MDC.setContextMap(context);
                try {
                    runnable.run();
                } finally {
                    MDC.clear();
                }
            };
        });
    }
}

应用到 所有 ThreadPoolTaskExecutor

public class MDCBeanPostProcessor implements InstantiationAwareBeanPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (bean instanceof ThreadPoolTaskExecutor) {
            MdcTaskExecutorCustomizer mdcTaskExecutorCustomizer = new MdcTaskExecutorCustomizer();
            ThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) bean;
            mdcTaskExecutorCustomizer.customize(executor);
            return true;
        }
        return InstantiationAwareBeanPostProcessor.super.postProcessAfterInstantiation(bean, beanName);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

MDCUtil
public abstract class MDCUtil {

    public static final String TRACE_ID = "traceId";

    public static String getTraceId() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    public static void startTrace() {
        MDC.put(TRACE_ID, UUID.randomUUID().toString().replaceAll("-", ""));
    }

    public static void startTrace(String traceId) {
        MDC.put(TRACE_ID, traceId);
    }

    public static void endTrace() {
        MDC.clear();
    }
}

servlet 环境

自定义 HandlerInterceptor

public class MDCMvcHandlerInterceptorInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        MDCUtil.startTrace();
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        MDCUtil.endTrace();
    }
}

reactive 环境(webflux)

public class MDCWebFilter implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        String traceId = MDCUtil.getTraceId();
        MDCUtil.startTrace(traceId);
        return chain.filter(exchange).subscriberContext(Context.of(MDCUtil.TRACE_ID, traceId)).doOnTerminate(MDCUtil::endTrace);
    }
}

自动装配

@Configuration
@ConditionalOnClass(ThreadPoolTaskExecutor.class)
public class MDCAutoConfiguration {
//
//    @Bean
//    public MdcTaskExecutorCustomizer mdcTaskExecutorCustomizer() {
//        return new MdcTaskExecutorCustomizer();
//    }

    @Bean
    public MDCBeanPostProcessor mdcBeanPostProcessor() {
        return new MDCBeanPostProcessor();
    }

    //====================================reactive=================================//
    @Bean
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
    public MDCWebFilter mdcWebFilter() {
        return new MDCWebFilter();
    }


    //=======================================servlet================================//

    @Configuration
    @ConditionalOnClass(DispatcherServlet.class)
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    public class InterceptorConfig implements WebMvcConfigurer {

        @Bean
        public HandlerInterceptor mdcMvcHandlerInterceptorInterceptor() {
            return new MDCMvcHandlerInterceptorInterceptor();
        }


        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(mdcMvcHandlerInterceptorInterceptor());
        }
    }
}

自定义注解
@EnabledTraceId
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MDCTracerImportSelector.class)
public @interface EnabledTraceId {

}
MDCTracerImportSelector
public class MDCTracerImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.x.z.stater.mdc.MDCAutoConfiguration"};
    }

}

使用

@EnabledTraceId
@SpringBootApplication
public class MvcApplication {

    public static void main(String[] args) {
        SpringApplication.run(MvcApplication.class, args);
    }
}

日志上加入 traceId


<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="30 seconds">
    <!--0. 日志格式和颜色渲染 -->
    <!-- 彩色日志依赖的渲染类 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
    <!-- 彩色日志格式 -->
    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} [%X{traceId}]: %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>


    <logger name="org.springframework.web" level="ERROR"/>
    <logger name="org.springboot.sample" level="ERROR" />
    <logger name="org.mybatis" level="INFO" />
    <logger name="org.springframework.data.mongodb.core" level="DEBUG"/>

    <!--1. 输出到控制台-->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <!-- 设置字符集 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <root level="DEBUG">
        <appender-ref ref="CONSOLE" />
    </root>

</configuration>

测试
@RestController
@Slf4j
public class MdcController {

    @Autowired
    private ThreadPoolTaskExecutor executor;

    @GetMapping("/run/mdc3")
    public String runMdc3() throws InterruptedException {
        log.info(Thread.currentThread().getName() + ": " + MDC.get("traceId"));
        executor.execute(() -> {
            log.info(Thread.currentThread().getName() + ": " + MDC.get("traceId"));
            executor.execute(() -> {
                log.info(Thread.currentThread().getName() + ": " + MDC.get("traceId"));
            });
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info(Thread.currentThread().getName() + ": " + MDC.get("traceId"));
        });
        TimeUnit.SECONDS.sleep(3);
        log.info(Thread.currentThread().getName() + ": " + MDC.get("traceId"));
        return "success";
    }

访问http://localhost:8088/run/mdc3:

c.y.m.m.controller.MdcController         : [4cd81770e58f42b8a3f40ab937ea394a]: http-nio-8088-exec-1: 4cd81770e58f42b8a3f40ab937ea394a
c.y.m.m.controller.MdcController         : [4cd81770e58f42b8a3f40ab937ea394a]: task-1: 4cd81770e58f42b8a3f40ab937ea394a
c.y.m.m.controller.MdcController         : [4cd81770e58f42b8a3f40ab937ea394a]: task-2: 4cd81770e58f42b8a3f40ab937ea394a
c.y.m.m.controller.MdcController         : [4cd81770e58f42b8a3f40ab937ea394a]: task-1: 4cd81770e58f42b8a3f40ab937ea394a
c.y.m.m.controller.MdcController         : [4cd81770e58f42b8a3f40ab937ea394a]: http-nio-8088-exec-1: 4cd81770e58f42b8a3f40ab937ea394a

可以发现,线程池中也可以传递traceId

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值