当前位置: 首页 > news >正文

MDC实现日志链路追踪

MDC是基于@Slf4j的

MDC是什么:(简单理解)线程上下文
日志链路追踪解决了什么:1:增强了代码的调试机制2:重点:实现了 多线程环境下 代码链路追踪

基础版本(不涉及异步)

1:引入日志依赖(slf4j的)

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId>
</dependency>

2:引入日志配置

📎logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.6.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.wm.file</groupId><artifactId>ExportExcelData</artifactId><version>1.0-SNAPSHOT</version><dependencies><!--Spring Boot 实现日志链路追踪,无需引入组件,让日志定位更方便!--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></dependency><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-annotation</artifactId><version>4.1.0</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>transmittable-thread-local</artifactId><version>2.14.2</version></dependency><!--测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.1</version><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

3:编写拦截器,基于MDC给请求添加TriceID

public class LogInterceptor implements HandlerInterceptor {private static final String TRACE_ID = "TRACE_ID";@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String tid = UUID.randomUUID().toString().replace("-", "");//可以考虑让客户端传入链路ID,但需保证一定的复杂度唯一性;如果没使用默认UUID自动生成if (!StringUtils.isEmpty(request.getHeader("TRACE_ID"))) {tid = request.getHeader("TRACE_ID");}MDC.put(TRACE_ID, tid);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) {MDC.remove(TRACE_ID);}}

原理详解

基础版问题(异步导致子线程triceID失效)

不涉及异步正常打印trice

异步方式会导致triceID失效

优化版:线程池解决异步问题(多加个线程池)

@Configuration
public class WebConfigurerAdapter implements WebMvcConfigurer {@Beanpublic LogInterceptor logInterceptor() {return new LogInterceptor();}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(logInterceptor()).addPathPatterns("/**");//可以具体制定哪些需要拦截,哪些不拦截,其实也可以使用自定义注解更灵活完成//  .excludePathPatterns("/testxx.html");}
}

package com.wm.file.config;import org.slf4j.MDC;import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;public final class ThreadMdcUtil {private static final String TRACE_ID = "TRACE_ID";// 获取唯一性标识public static String generateTraceId() {return UUID.randomUUID().toString();}public static void setTraceIdIfAbsent() {if (MDC.get(TRACE_ID) == null) {MDC.put(TRACE_ID, generateTraceId());}}/*** 用于父线程向线程池中提交任务时,将自身MDC中的数据复制给子线程** @param callable* @param context* @param <T>* @return*/public static <T> Callable<T> wrap(final Callable<T> callable, final Map<String, String> context) {return () -> {if (context == null) {MDC.clear();} else {MDC.setContextMap(context);}setTraceIdIfAbsent();try {return callable.call();} finally {MDC.clear();}};}/*** 用于父线程向线程池中提交任务时,将自身MDC中的数据复制给子线程** @param runnable* @param context* @return*/public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {return () -> {if (context == null) {MDC.clear();} else {MDC.setContextMap(context);}setTraceIdIfAbsent();try {runnable.run();} finally {MDC.clear();}};}
}
package com.wm.file.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;import java.util.concurrent.Executor;@Configuration
@EnableAsync
public class ThreadPoolConfig {/*** 声明一个线程池** @return 执行器*/@Bean("MyExecutor")public Executor asyncExecutor() {MyThreadPoolTaskExecutor executor = new MyThreadPoolTaskExecutor();//核心线程数5:线程池创建时候初始化的线程数executor.setCorePoolSize(5);//最大线程数5:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程executor.setMaxPoolSize(5);//缓冲队列500:用来缓冲执行任务的队列executor.setQueueCapacity(500);//允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁executor.setKeepAliveSeconds(60);//线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池executor.setThreadNamePrefix("asyncJCccc");executor.initialize();return executor;}
}
@Configuration
public class WebConfigurerAdapter implements WebMvcConfigurer {@Beanpublic LogInterceptor logInterceptor() {return new LogInterceptor();}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(logInterceptor()).addPathPatterns("/**");//可以具体制定哪些需要拦截,哪些不拦截,其实也可以使用自定义注解更灵活完成//  .excludePathPatterns("/testxx.html");}
}

加入这四个线程操作文件,

异步执行父线程将triceID传递给子线程

启动加载顺序

请求进来的流程:

1:给线程设置triceID

2:感知到异步(@aysc)触发线程池(我们重写的)

3:父线程赋值TriceID给子线程(子线程调用携带父线程的triceID)

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 高性能计算应用优化之代码实现调优(一)
  • TypeScript Agenda异常 undefined (reading ‘collection‘)
  • word中怎么快速选中光标之前或之后的全部内容?
  • 二、Maven工程的构建--JavaSEJavaEE
  • 软考高级:系统架构设计师——软件架构设计 Chapter 笔记
  • Redis持久化机制—RDB与AOF
  • 海外云手机是否适合运营TikTok?
  • GitLab管理之迁移GitLab群组
  • 基础闯关5
  • ceph-iscsi 手动安装过程中的一些问题记录以及解决办法
  • C# 通过拖控件移动窗体
  • 传统CV算法——基于Opencv的图像绘制
  • ARM编程模型、指令集、ARM汇编语言程序设计
  • Mthrift服务调度
  • Robotframework框架基础
  • 【159天】尚学堂高琪Java300集视频精华笔记(128)
  • 【知识碎片】第三方登录弹窗效果
  • 78. Subsets
  • AngularJS指令开发(1)——参数详解
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • Git学习与使用心得(1)—— 初始化
  • MD5加密原理解析及OC版原理实现
  • Octave 入门
  • Promise初体验
  • Sequelize 中文文档 v4 - Getting started - 入门
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • uva 10370 Above Average
  • vue总结
  • WePY 在小程序性能调优上做出的探究
  • 关于Android中设置闹钟的相对比较完善的解决方案
  • 汉诺塔算法
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 面试遇到的一些题
  • 前端之Sass/Scss实战笔记
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • 探索 JS 中的模块化
  • 格斗健身潮牌24KiCK获近千万Pre-A轮融资,用户留存高达9个月 ...
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • #职场发展#其他
  • (Java数据结构)ArrayList
  • (Note)C++中的继承方式
  • (笔记)M1使用hombrew安装qemu
  • (超详细)语音信号处理之特征提取
  • (定时器/计数器)中断系统(详解与使用)
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (四)Android布局类型(线性布局LinearLayout)
  • (转)iOS字体
  • (转)程序员疫苗:代码注入
  • (转)重识new
  • (转载)从 Java 代码到 Java 堆
  • .equals()到底是什么意思?
  • .net core 6 redis操作类
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .Net 基于.Net8开发的一个Asp.Net Core Webapi小型易用框架