MDC 实现 traceId 记录

目的

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

通用

ThreadPoolTaskExecutor 实现 MDC 传递

public class MdcTaskExecutorCustomizer implements TaskExecutorCustomizer {@Overridepublic 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;@Overridepublic 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);}@Overridepublic 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 {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {MDCUtil.startTrace();return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {MDCUtil.endTrace();}
}

reactive 环境(webflux)

public class MDCWebFilter implements WebFilter {@Overridepublic 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();
//    }@Beanpublic 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 {@Beanpublic HandlerInterceptor mdcMvcHandlerInterceptorInterceptor() {return new MDCMvcHandlerInterceptorInterceptor();}@Overridepublic 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 {@Overridepublic 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 {@Autowiredprivate 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

MDC 实现 traceId 记录相关推荐

  1. 使用MDC增强日志记录

    文章目录 (一)日志框架 1. 日志框架介绍和选择 2. 原理介绍 3. SpringBoot日志框架 (二)使用MDC增强日志记录 1. 介绍 2. 普通示例 3. 在Log4j中使用MDC 4. ...

  2. LogBack sl4j 通过MDC实现日志记录区分用户Session[以Spring mvc为例]

    1.首先实现一个interceptor,在请求开始的时候MDC put一个Session标志,interceptor结束的时候remove掉 import javax.servlet.http.Htt ...

  3. 分布式链路追踪—traceId生成与使用(MDC)

    之前遇到过在分布式环境中不方便查找日志的问题,虽然现在的很多云服务环境都提供这种功能,但是在自己搭建的分布式环境中可以考虑应急的这样做,先记录下,以便下次快速使用 注意:MDC-traceId在分布式 ...

  4. 简单记录使用org.slf4j.MDC进行日志追踪

    日志追踪 依赖 配置文件 代码 controller service 控制台打印 源码追踪 依赖 这里使用的是log4j2 <parent><groupId>org.sprin ...

  5. MDC日志logback整合使用

    MDC日志logback整合使用 为什么使用MDC记录日志? 场景: 由于我的搜索服务并发量比较高,而处理一次搜索请求需要记录多个日志,因此日志特别多的情况下去查一次搜索整个日志打印情况会比较复杂. ...

  6. 几行代码轻松搞定跨系统传递 traceId

    同样是新项目开发的笔记,因为使用的是分布式架构,涉及到各个系统之间的交互 这时候就会遇到一个很常见的问题: 单个系统是集群部署,日志分布在多台服务器上: 多个系统的日志在多台机器,但是一次请求,查日志 ...

  7. 链路日志traceId

    项目查日志太麻烦,多台机器之间查来查去,还不知道是不是同一个请求的.打印日志时使用 MDC 在日志上添加一个 traceId,使用 traceId 跨系统传递 1 背景 同样是新项目开发的笔记,因为使 ...

  8. 链路日志中追踪traceId

    一,使用traceId概述 平时出现问题查询日志是程序员的解决方式,日志一般会从服务器的日志文件,然后查找自己需要的日志,或者日志输出到es中,在es中进行搜索日志,可是对于目前流行的微服务或者单体服 ...

  9. slf4j的MDC机制

    MDC(Mapped Diagnostic Context)诊断上下文映射,是slf4j提供的一个支持动态打印日志信息的工具,举例来说,对于一个web Server,如果服务端想在打印出的每行日志中都 ...

最新文章

  1. 计算机专业PhD申请文书范文,美国留学博士申请文书怎么写之范文分享
  2. 深入理解Java中的final关键字
  3. vim实用配置(转)
  4. Tensorflow:Tensorboard使用
  5. PHPstudy新版小P面板进行设置版本
  6. Django远端访问
  7. TCP协议的三次握手和四次分手
  8. Linux:tomcat安装/版本升级
  9. 全栈工程师对Python面试中is和==的区别做出解释!看完真的学到了
  10. python爬虫程序框架的理论是什么_Python网络爬虫(scrapy框架简介和基础应用)
  11. linux系统中find怎么用,Linux系统中查找命令find的使用方法(一)
  12. 服务器系统2008网络发现,Windows Server 2008中解决局域网共享发现问题
  13. 代码整洁之道——如何写出整洁高效的代码
  14. 护士副高需要计算机考试吗,护士评副高什么要求
  15. 选队长游戏(Java)
  16. mysql 数据库里查询语句中不等于的两种写法
  17. IDEA全局 查找/搜索 代码里所有 中文/汉字 或指定的 中文/汉字
  18. 图文详解丨iOS App上架全流程及审核避坑指南
  19. Leetcode PHP题解--D70 784. Letter Case Permutation
  20. 企业信息化系统ERP篇

热门文章

  1. mysqldump逻辑备份
  2. 福昕阅读器给pdf创建目录方法
  3. MySQL WEEK函数介绍
  4. ThinkPHP5.0 中使用荣联云通讯
  5. Vue和React的区别到底是什么?
  6. 查看linux操作系统版本--实用
  7. Ubuntu 安装 NVIDIA 显卡驱动详细步骤(ERROR: The Nouveau kernel driver is currently in use by your system)
  8. 9个国外最佳免费编程学习一站式网站,谁用谁知道!
  9. word文档中设置的行距一样但实际显示的不一样
  10. 用于机器学习的 NumPy(ML)