一、请求链路追踪是什么?

能标识一次请求的完整流程,包括日志打印、响应标识等,以便于出现问题可以快速定位并解决问题。

二、使用步骤

1. 相关知识点

  1. ThreadLocal:一种保证一种规避多线程访问出现线程不安全的方法,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全问题。
  2. MDC:(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能,基于ThreadLocal实现的一种工具类。
  3. 拦截器:基于拦截器对每个请求注入traceId。

2. 代码实现

  1. 封装TraceId工具类:
/*** @author yinfeng* @description traceId工具类* @since 2021/10/2 11:10*/
public class TraceIdUtil {private static final String TRACE_ID = "traceId";public static void set() {MDC.put(TRACE_ID, generate());}public static String get() {return MDC.get(TRACE_ID);}public static void remove() {MDC.remove(TRACE_ID);}public static String generate() {return UUID.randomUUID().toString().replace("-", "").substring(0, 16);}
}

  1. springboot环境注入工具类
/*** @author yinfeng* @description 资源配置工具类* @since 2021/10/2 0:02*/
public class PropertySourcesUtil {private static final String NAME = "aop.yinfeng";private static ConfigurableEnvironment environment;private static SpringApplication application;public static void setEnvironment(ConfigurableEnvironment environment) {if (PropertySourcesUtil.environment == null) {PropertySourcesUtil.environment = environment;}}public static SpringApplication getApplication() {return application;}public static void setApplication(SpringApplication application) {PropertySourcesUtil.application = application;}public static void set(String key, Object value) {getSourceMap().put(key, value);}public static Object get(String key) {return getSourceMap().get(key);}public static Map<String, Object> getSourceMap() {PropertySource<?> propertySource = environment.getPropertySources().get(NAME);Map<String, Object> source;if (propertySource == null) {source = new LinkedHashMap<String, Object>();propertySource = new MapPropertySource(NAME, source);environment.getPropertySources().addLast(propertySource);}source = (Map<String, Object>) propertySource.getSource();return source;}
}

  1. 支持配置的日志实体类:
/*** @author yinfeng* @description 日志配置类* @since 2021/10/1 17:45*/
@Data
@ConfigurationProperties(prefix = "aop.logging")
public class LogProperties {private String logDir;// 因为logback和log4j的日志格式略有不同,所以提供2种打印格式private String logbackPattern = "%d{yyyy-MM-dd HH:mm:ss.SSS} %X{traceId} %-5level %logger{30} : %msg%n";private String log4jPattern = "%d{yyyy-MM-dd HH:mm:ss.SSS} %X{traceId} %-5level %clr{%-30.30c{1.}}{cyan} : %msg%n";
}

  1. 环境增强注入配置:因为请求链路追踪在各个服务中比较常用,所以以starter的形式进行封装,在spring环境加载后进行配置注入。
/*** @author yinfeng* @description 环境注入抽象类* @since 2021/10/1 17:55*/
public abstract class AbstractEnvironmentPostProcessor implements EnvironmentPostProcessor {private static final String DEV = "dev";private static final String STG = "stg";private static final String PRD = "prod";@Overridepublic void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {PropertySourcesUtil.setEnvironment(environment);final List<String> profiles = Arrays.asList(environment.getActiveProfiles());if (profiles.contains(PRD)) {doPrd(environment, application);} else if (profiles.contains(STG)) {doStg(environment, application);} else {doDev(environment, application);}onProfile(environment, application);}protected void doPrd(ConfigurableEnvironment environment, SpringApplication application) {}protected void doStg(ConfigurableEnvironment environment, SpringApplication application) {}protected void doDev(ConfigurableEnvironment environment, SpringApplication application) {}protected void onProfile(ConfigurableEnvironment environment, SpringApplication application) {}
}
/*** @author yinfeng* @description 日志环境注入* @since 2021/10/1 17:52*/
@EnableConfigurationProperties(LogProperties.class)
public class LogEnvAdvice extends AbstractEnvironmentPostProcessor {@Overrideprotected void onProfile(ConfigurableEnvironment environment, SpringApplication application) {final Binder binder = Binder.get(environment);final BindResult<LogProperties> bindResult = binder.bind("aop.logging", Bindable.of(LogProperties.class));LogProperties logProperties = new LogProperties();if (bindResult.isBound()) {logProperties = bindResult.get();}// 配置日志打印格式if (isLogback(application)) {PropertySourcesUtil.set("logging.pattern.console", logProperties.getLogbackPattern());PropertySourcesUtil.set("logging.pattern.file", logProperties.getLogbackPattern());return;}PropertySourcesUtil.set("logging.pattern.console", logProperties.getLog4jPattern());PropertySourcesUtil.set("logging.pattern.file", logProperties.getLog4jPattern());}/*** 判断是否是logback日志格式** @param application application* @return*/private boolean isLogback(SpringApplication application) {final LoggingSystem loggingSystem = LoggingSystem.get(application.getClassLoader());return LogbackLoggingSystem.class.equals(loggingSystem.getClass());}
}

  1. 在spring.factory文件配置log环境注入类
org.springframework.boot.env.EnvironmentPostProcessor=com.yinfeng.common.enviroment.LogEnvAdvice

  1. 配置拦截器,在每个请求进入时注入traceId,因为基于threadLocal实现,所以需要在请求完成后进行手动清除,否则gc会扫描不到
/**
* @author yinfeng
* @description 日志拦截器
* @since 2021/10/2 11:09
*/
public class LogInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {TraceIdUtil.set();return true;}/*** 回收资源,防止oom* @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {TraceIdUtil.remove();}
}
/*** @author yinfeng* @description 拦截器增强* @since 2021/10/2 11:15*/
public class InterceptorAdvice implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 将拦截器注入到容器中final InterceptorRegistration registration = registry.addInterceptor(new LogInterceptor()).order(Integer.MIN_VALUE);registration.addPathPatterns("/**");}
}

3. 测试一下效果

到此为止,通过traceId追踪请求链路代码基本完成,下面咱们来测认识一下

  1. 在pom文件中引入咱们的starter
<dependency><groupId>com.yinfeng</groupId><artifactId>common-starter</artifactId><version>1.0.0</version><exclusions><exclusion><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId></exclusion><exclusion><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId></exclusion></exclusions>
</dependency>
  1. 通过knife4j接口文档发送请求
  2. 查看日志:可以看到咱们所有的业务日志打印都会带上traceId,方便咱们快速定位问题

三、总结:下一节咱们来说对全局响应体包装和traceId链路追踪的结合。都看到这里了,麻烦各位老铁给个赞吧。

Springboot starter开发之traceId请求日志链路追踪相关推荐

  1. android 接口实现方法,Android应用开发之Android 请求网络接口实现方法

    本文将带你了解Android应用开发之Android 请求网络接口实现方法,希望本文对大家学Android有所帮助. public   class Fragment01 extends Fragmen ...

  2. 微服务架构 | 如何利用好日志链路追踪做性能分析?

    导读:做性能分析听到最多的歪理就是,服务做水平.垂直扩容.分表分库.读写分离.XX中间件.资源静态化等等但是归根到底这些方案都是为了尽可能减少对数据库的访问以及堆栈的释放,提高数据库IO的读写速度和程 ...

  3. MDC实现日志链路追踪

    开发过程中难免遇到需要查看日志来找出问题出在哪一环节的情况,而在实际情况中服务之间互相调用所产生的日志冗长且复杂,若是再加上同一时间别的请求所产生的日志,想要精准定位自己想要查看的日志就比较麻烦.为解 ...

  4. 手动实现 SpringBoot 日志链路追踪,无需引入组件,日志定位更方便!

    点击关注公众号,实用技术文章及时了解 前言 从文章标题就知道,这篇文章是介绍些什么. 这是我一位朋友的问题反馈: 好像是的,确实这种现象是普遍存在的. 有时候一个业务调用链场景,很长,调了各种各样的方 ...

  5. Dubbo快速入门 —— 基于SpringBoot Starter 开发微服务应用案例 + 知识讲解

  6. SpringBoot: Web开发之Listener实践

    文章目录 Listener简介 Listener实践 Listener简介 其实很简单就是一个机制,简单来说就是:比如你监听了一个按钮,那个当这个按钮被点击或者鼠标滑过的时候,就会被监听到,你可以对此 ...

  7. 链路日志中追踪traceId

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

  8. 日志排查问题困难?分布式日志链路跟踪来帮你

    点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 作者:朱乐陶,软件架构师,具备多年Java开发及架构设计经验,擅长微服务领域 作者博客:htt ...

  9. 赛题解析 | 初赛赛道一:实现一个分布式统计和过滤的链路追踪

    简介: 首届云原生编程挑战赛正在报名中,初赛共有三个赛道,本文主要针对赛道三题目做出剖析,帮助选手更高效的解题. 首届云原生编程挑战赛正在报名中,初赛共有三个赛道,题目如下: 赛道一:实现一个分布式统 ...

最新文章

  1. 解决远程登录MYSQL数据库
  2. Ubuntu系统---NVIDIA 驱动安装
  3. [Recompose] Render Nothing in Place of a Component using Recompose
  4. linux文件 run.man,【Linux】linux经常使用基本命令
  5. MySQL日期时间函数大全(转)
  6. 【远程办公】5分钟一拍照、10分钟一截屏 ?
  7. “编程能力差,90%输在了选择上!”CTO:多数程序员都是瞎努力!
  8. 史上最便捷搭建 Zookeeper 的方法!
  9. VC++6.0安装番茄助手Visual Assist X和VC6LineNumberAddin的方法
  10. 最新win10系统下载64位
  11. docker和k8s的常见命令
  12. 八爪鱼批量爬取html中的数据,批量采集网页数据 - 八爪鱼采集器
  13. 设置计算机关机时间快捷键,电脑怎么设定关机时间?
  14. TriangleCount三角形计数
  15. NLP工具——Stanza依存关系含义详解
  16. iOS-Pods-XX.debug.xcconfig: unable to open file
  17. Jmeter-----保存到响应文件
  18. 张艾迪(创始人):23岁天才的创业史
  19. 实验二 插值方法(android)
  20. HDWiki数据库结构说明

热门文章

  1. NameError: name 'sys' is not defined
  2. react循环的值为什么要有key_糊盒粘箱为什么要检查表面覆膜电晕值
  3. springboot 整合redis_springboot自动装配原理详解
  4. 首发Android 13!谷歌Pixel 7 Pro渲染图曝光:后置相机模组吸睛
  5. 官方揭晓iQOO 9正反面宣传照:挖孔直屏+巨型三摄相机模组
  6. 华为P50系列确定29日发布:但遗憾的是...
  7. 威马EX6 Plus探险之旅!房山郊区竟藏着个1万平超大废弃矿洞?
  8. 全网沸腾!鸿蒙手机要来了
  9. 1年狂赚500亿!中国最土豪的省,究竟是如何称霸全球的?
  10. 华为金融业务部总裁曹冲:没有进入支付市场的计划