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

SpringBoot项目注入 traceId 来追踪整个请求的日志链路

SpringBoot项目注入 traceId 来追踪整个请求的日志链路,有了 traceId, 我们在排查问题的时候,可以迅速根据 traceId 查找到相关请求的日志,特别是在生产环境的时候,用户可能只提供一个错误截图,我们作为开发人员想要更详细的日志,这时候我们就可以直接根据用户提供的 traceId 来查找用户这次请求的整个日志,会方便很多。

springboot 单体项目没办法做到有 traceId 一样的效果吗?

即使是在单体应用中,为请求添加一个唯一的traceId以追踪整个请求链路的日志也是完全可行的。虽然Spring Cloud Sleuth等工具主要用于分布式系统中的链路追踪,但在单体Spring Boot应用中你仍然可以通过一些方法来实现类似的功能。下面是一个基本的方法来手动实现这样的功能:

手动实现 traceId 注入

1. 创建一个过滤器或拦截器:你可以创建一个Servlet过滤器或者Spring的拦截器,用于在每个请求到达时生成一个唯一的traceId(如果不存在的话),并将其存储到当前线程的上下文中。

2. 使用MDC(Mapped Diagnostic Context):SLF4J提供了MDC工具,可以用来保存日志的上下文信息。你可以在过滤器或拦截器中将traceId放入MDC中,这样所有的日志记录器都可以自动包含这个ID。 示例代码:

TraceIdFilter :

# 我这里用的是 jdk17, 所以 servlet 工具类被移动到了 jakarta 包下,如果你用的是比较老的 jdk, 可以删除后重新导入 servlet 的类。
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;import java.io.IOException;
import java.util.UUID;
@Compone
nt
public class TraceIdFilter extends OncePerRequestFilter {private static final String TRACE_ID = "traceId";@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {String traceId = request.getHeader(TRACE_ID);if (traceId == null) {traceId = UUID.randomUUID().toString();}MDC.put(TRACE_ID, traceId);try {filterChain.doFilter(request, response);} finally {MDC.remove(TRACE_ID);}}
}

3. 配置日志格式:确保你的日志配置文件(如logback.xml、log4j2.xml等)中包含了输出traceId的配置。例如,在logback中,你可以这样做:

<configuration><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss} - [%thread] %-5level %logger{36} - [%X{traceId}] - %msg%n</pattern></encoder></appender><root level="info"><appender-ref ref="CONSOLE"/></root>
</configuration>

我的项目使用的是 logback-spring.xml ,我的文件完整内容如下:
logback-spring.xml:

<configuration debug="true"><!-- 引用Spring Boot全局配置文件中的日期格式配置项 --><springProperty scope="context" name="dateformat" source="logging.pattern.dateformat" defaultValue="yyyy-MM-dd HH:mm:ss.SSS"/><springProperty scope="context" name="APP_NAME"  source="spring.application.name"/><springProperty scope="context" name="LOG_FILE"  source="logging.file.path"/><!-- 定义颜色编码 --><conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/><conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/><!-- 定义日志输出到控制台的appender --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%clr(%d{${dateformat}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr([%15.15t]){faint} [%-40.40logger{39}] [%X{traceId}] %clr(%msg%n){faint}%wEx</pattern></encoder></appender><!-- 开发环境配置 --><springProfile name="local | test"><root level="DEBUG"><appender-ref ref="STDOUT" /></root><!-- 开发环境专属的详细日志 --><logger name="com.tylerzhong" level="TRACE" additivity="false"><appender-ref ref="STDOUT" /></logger></springProfile><springProfile name="dev | prod"><!-- 文件日志:输出全部日志到文件 --><appender name="FILE_INFO_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${LOG_FILE}/${APP_NAME}.log</file><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>ERROR</level><onMatch>ACCEPT</onMatch><onMismatch>ACCEPT</onMismatch></filter><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_FILE}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log</fileNamePattern><maxHistory>180</maxHistory><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>50MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy></rollingPolicy><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>%d{${dateformat}} [%thread] %-5level %logger{36} [%X{traceId}] - %msg%n</pattern><charset>UTF-8</charset></encoder></appender><!-- 错误日志:用于将错误日志输出到独立文件 --><appender name="FILE_ERROR_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${LOG_FILE}/error-${APP_NAME}.log</file><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>ERROR</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_FILE}/error-${APP_NAME}.%d{yyyy-MM-dd}.%i.log</fileNamePattern><maxHistory>180</maxHistory><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>50MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy></rollingPolicy><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>%d{${dateformat}} [%thread] %-5level %logger{36} [%X{traceId}] - %msg%n</pattern><charset>UTF-8</charset></encoder></appender><root level="INFO"><appender-ref ref="STDOUT" /><appender-ref ref="FILE_INFO_LOG" /><appender-ref ref="FILE_ERROR_LOG" /></root></springProfile></configuration>

关于更具体的日志输出配置,可以参考我的这篇文章《SpringBoot 项目配置日志输出》。

通过这种方式,你就可以在单体Spring Boot项目中为每个请求生成一个唯一的traceId,并通过日志输出它,从而实现在处理请求期间的所有日志条目都能关联起来的效果。这种方法不仅适用于单体应用,也可以作为理解更复杂的分布式追踪概念的基础。

这样配置之后,你在代码中打印日志的时候就会携带上 traceId 了。

 log.info("请求参数: {}", arg);

输出示例:

2025-02-26 19:25:02.187  INFO [nio-8070-exec-2] [com.tylerzhong.web.config.LoggingAspect       ] [ab85097a-6d31-44c3-bcfb-2bcc97b87ab9] 方法: TransferController.xxx(..)

当然,我们不止要将 traceId 输出到控制台,还需要将 traceId 返回给前端用户,这样用户找我们排查问题的时候,只需要给一个 traceId 给我们即可。

所以在返回的包装类中注入 traceId 返回给前端。
我的代码如下:

import lombok.Getter;
import lombok.NoArgsConstructor;
import org.slf4j.MDC;@Getter
@NoArgsConstructor
public class Result<T> {private int code;private boolean flag;private String desc;private String cause;private String traceId;private T data;public Result(int code, boolean flag, String desc) {this.code = code;this.flag = flag;this.desc = desc;}public Result(int code, boolean flag, String desc, String traceId) {this.code = code;this.flag = flag;this.desc = desc;this.traceId = traceId;}public Result(int code, boolean flag, String desc, String cause, String traceId) {this.code = code;this.flag = flag;this.desc = desc;this.cause = cause;this.traceId = traceId;}public Result(int code, boolean flag, String desc, T data, String traceId) {this.code = code;this.flag = flag;this.desc = desc;this.data = data;this.traceId = traceId;}public static <T> Result<T> success() {return new Result<>(0, true, "成功", MDC.get("traceId"));}public static <T> Result<T> success(T data) {return new Result<>(0, true,"成功", data, MDC.get("traceId"));}public static <T> Result<T> fail(int code, String desc, String cause) {return new Result<>(code, false, desc, cause, MDC.get("traceId"));}
}

返回给前端的失败示例:

{"code": 405,"flag": false,"desc": "请求方式不支持.","cause": "Request method 'POST' is not supported","traceId": "ebd77e99-361c-47b3-8569-2d321d011418","data": null
}

返回给前端的成功示例:

{"code": 0,"flag": true,"desc": "成功","cause": null,"traceId": "07a3b99c-8cf8-45dc-a758-6c7636472cab","data": {"count": 100,"name": "张三"}
}

扩展

打印日志内容,你很可能会涉及到 Spring 的 AOP 切面打印日志。可以参考这篇文章《Spring AOP 切面打印日志完整版》

相关文章:

SpringBoot项目注入 traceId 来追踪整个请求的日志链路

SpringBoot项目注入 traceId 来追踪整个请求的日志链路&#xff0c;有了 traceId&#xff0c; 我们在排查问题的时候&#xff0c;可以迅速根据 traceId 查找到相关请求的日志&#xff0c;特别是在生产环境的时候&#xff0c;用户可能只提供一个错误截图&#xff0c;我们作为开发…...

【Block总结】SAFMN,空间自适应调制与局部特征增强的协同设计|即插即用

论文信息 标题&#xff1a;Spatially-Adaptive Feature Modulation for Efficient Image Super-Resolution论文链接&#xff1a;https://arxiv.org/pdf/2302.13800代码与模型&#xff1a;https://github.com/sunny2109/SAFMN 创新点 空间自适应特征调制&#xff08;SAFM&…...

Python爬虫:一文掌握PyQuery模块

文章目录 1. PyQuery 简介2. PyQuery 的安装2.1 安装 PyQuery2.2 安装依赖库3. PyQuery 的基本使用3.1 初始化 PyQuery 对象3.2 选择元素3.3 获取元素内容3.4 遍历元素4. PyQuery 的高级用法4.1 过滤元素4.2 查找子元素4.3 获取属性值4.4 修改元素4.5 添加和删除元素4.6 遍历文…...

LearnOpenGL之Shader编程用算法绘画

———————————————————— 前序 ——————————————————— AndroidLearnOpenGL是本博主自己实现的LearnOpenGL练习集合&#xff1a; Github地址&#xff1a;GitHub - wangyongyao1989/AndroidLearnOpenGL: OpenGL基础及运用 系列文章&#xff…...

如何使用Spring Boot框架整合Redis:超详细案例教程

目录 # 为什么选择Spring Boot与Redis整合&#xff1f; 1. 更新 pom.xml 2. 配置application.yml 3. 创建 Redis 配置类 4. Redis 操作类 5. 创建控制器 6. 启动应用程序 7. 测试 # 为什么选择Spring Boot与Redis整合&#xff1f; 将Spring Boot与Redis整合可以充分利…...

算法--贪心

贪心 原理经典例题[860. 柠檬水找零](https://leetcode.cn/problems/lemonade-change/description/)[2208. 将数组和减半的最少操作次数](https://leetcode.cn/problems/minimum-operations-to-halve-array-sum/description/)[179. 最大数](https://leetcode.cn/problems/large…...

线程控制(创建、终止、等待、分离)

目录 1.前言 2.创建线程 pthread_create函数 3.线程终止 pthread_exit函数 pthread_cancel函数 4.线程等待 5.线程分离 1.前言 在Linux系统中&#xff0c;并不存在真正的线程&#xff0c;只有轻量级进程。所以&#xff0c;Linux系统只提供了操作轻量级进程的系统调用…...

【备份】php项目处理跨域请求踩坑

这都是老生常谈的东西了。我还在踩坑&#xff0c;记录一下。 我在项目入口明明写了如下代码&#xff1a; // 处理预检请求 (OPTIONS) if ($_SERVER[REQUEST_METHOD] OPTIONS) {header("Access-Control-Allow-Origin: https://xxx.vip");header("Access-Cont…...

目标检测YOLO实战应用案例100讲-面向无人机图像的小目标检测

目录 知识储备 YOLO v8无人机拍摄视角小目标检测 数据集结构 环境部署说明 安装依赖 模型训练权重和指标可视化展示 训练 YOLOv8 PyQt5 GUI 开发 主窗口代码 main_window.py 使用说明 无人机目标跟踪 一、目标跟踪的基本原理 二、常用的目标跟踪算法 基于YOLOv…...

实现 Leaflet 多类型点位标记与聚合功能的实战经验分享

在现代的地理信息系统&#xff08;GIS&#xff09;应用中&#xff0c;地图功能是不可或缺的一部分。无论是展示商业网点、旅游景点还是公共服务设施&#xff0c;地图都能以直观的方式呈现数据。然而&#xff0c;当数据量较大时&#xff0c;地图上可能会出现大量的标记点&#x…...

Linux 环境“从零”部署 MongoDB 6.0:mongosh 安装与数据操作全攻略

前提 完成linux平台部署MongoDB【部署教程】且完成mongosh的安装 由于本人使用的是6.0版本的MongoDB&#xff0c;新版本 MongoDB&#xff08;尤其是 6.0 及以上版本&#xff09;已经不再默认捆绑传统的 mongo shell&#xff0c;而改用新的 MongoDB Shell&#xff08;mongosh&am…...

深度学习五大模型:CNN、Transformer、BERT、RNN、GAN详细解析

# 深度学习五虎将&#xff1a;当CNN遇见Transformer的奇幻漂流 ## 序章&#xff1a;AI江湖的兵器谱排行 2012年&#xff0c;多伦多大学的厨房里&#xff0c;Hinton的学生们用GPU煎了个"AlexNet"荷包蛋&#xff0c;从此开启了深度学习的热兵器时代。如今五大模型各显…...

004 rocketmq集群

1、集群模式 在RocketMQ中&#xff0c;集群的部署模式是比较多的&#xff0c;有以下几种&#xff1a; public class ConsumerDemo {public static void main(String[] args) throws Exception {DefaultMQPushConsumer consumer new DefaultMQPushConsumer("test-group&qu…...

基于 Python 深度学习的电影评论情感分析可视化系统(2.0 全新升级)

基于 Python 深度学习的电影评论情感分析可视化系统&#xff0c;基于 Flask 深度学习&#xff0c;构建了一个 影评情感分析系统&#xff0c;能够 自动分析影评、计算情感趋势 并 可视化展示&#xff0c;对于电影行业具有重要参考价值&#xff01; 基于 Python 深度学习的电影评…...

Linux内核配置与构建原理

Kconfig文件 Kconfig是Linux内核中用于配置功能的脚本语言系统&#xff0c;由众多内核源码树中每个目录下的Kconfig文件组成。它定义Linux相关的配置选项层次结构和依赖关系。 menuconfig工具&#xff0c;会抓取Kconfig中的信息&#xff0c;为用户输出友好的交互式菜单选项配…...

大语言模型微调的基本概念介绍

大型语言模型&#xff08;LLMs&#xff09;正在以惊人的速度发展&#xff0c;LLM微调的潜力更是如此。大型语言模型的生命周期有几个关键步骤&#xff0c;今天我们将要介绍这个周期中最丰富、最耗时的一部分——LLM微调过程。 大语言模型的生命周期 在深入了解大型语言模型&a…...

实例分割 | yolov11训练自己的数据集

前言 因工作要求使用的都是yolov5系列的模型&#xff0c;今天学习一下最先进的yolov11&#xff0c;记录一下环境配置及训练过程。 1.项目下载及环境安装 源码位置&#xff1a;yolov11 可以看到&#xff0c;这里要求python版本大于等于3.8&#xff0c;我这里安装python3.10.…...

vue3:四嵌套路由的实现

一、前言 1、嵌套路由的含义 嵌套路由的核心思想是&#xff1a;在某个路由的组件内部&#xff0c;可以定义子路由&#xff0c;这些子路由会渲染在父路由组件的特定位置&#xff08;通常是 <router-view> 标签所在的位置&#xff09;。通过嵌套路由&#xff0c;你可以实…...

AIGC和搜索引擎的异同

AIGC&#xff08;生成式人工智能&#xff09;与搜索引擎的核心差异体现在信息处理方式和输出形态上&#xff0c;我们可以从以下维度对比&#xff1a; 一、工作原理的本质差异 信息检索机制 搜索引擎&#xff1a;基于关键词匹配&#xff08;如"中暑怎么办"→返回相关…...

ES批量查询

在 Elasticsearch 中&#xff0c;multi_search&#xff08;也称为 msearch&#xff09;是一种允许你在单个请求中执行多个搜索操作的 API。它可以显著减少网络开销&#xff0c;尤其是在需要执行多个查询时。multi_search 会将多个查询打包成一个请求发送给 Elasticsearch&#…...

从MATLAB到C++:手把手教你将鱼眼相机标定结果(Scaramuzza模型)部署到OpenCV项目

从MATLAB到C&#xff1a;手把手教你将鱼眼相机标定结果&#xff08;Scaramuzza模型&#xff09;部署到OpenCV项目 鱼眼相机因其超广视角在机器人导航、虚拟现实等领域应用广泛。但MATLAB标定结果如何无缝迁移到C工程环境&#xff1f;本文将彻底解决这个痛点&#xff0c;带您完成…...

极客玩法:OpenClaw+Qwen3-14B控制智能家居实战

极客玩法&#xff1a;OpenClawQwen3-14B控制智能家居实战 1. 为什么选择OpenClaw控制智能家居&#xff1f; 去年装修新房时&#xff0c;我给自己定了个小目标&#xff1a;所有智能设备必须能通过自然语言控制。市面上的语音助手总让我觉得"差点意思"——要么响应慢…...

Spring AI实战:5分钟搞定豆包TTS语音合成(附完整Java代码)

Spring AI实战&#xff1a;5分钟集成豆包TTS语音合成&#xff08;附完整Java代码&#xff09; 语音合成技术正在重塑人机交互的边界。作为Java开发者&#xff0c;你可能已经注意到Spring AI生态的快速崛起——它正成为企业级AI应用开发的新标准。本文将带你用最短时间完成豆包T…...

C标准库缓冲区溢出问题与安全编程实践

1. C标准库缓冲区溢出问题概述缓冲区溢出是C语言开发中最常见的安全漏洞之一。作为一名有十年经验的嵌入式开发者&#xff0c;我见过太多由于不当使用标准库函数导致的崩溃和安全问题。缓冲区溢出通常发生在程序向固定大小的缓冲区写入超过其容量的数据时&#xff0c;多余的数据…...

intv_ai_mk11步骤详解:从curl验证到浏览器交互,完整闭环操作演示

intv_ai_mk11步骤详解&#xff1a;从curl验证到浏览器交互&#xff0c;完整闭环操作演示 1. 模型概述与核心能力 intv_ai_mk11是基于Llama架构的中等规模文本生成模型&#xff0c;专为通用文本处理任务优化。这个开箱即用的解决方案特别适合以下场景&#xff1a; 智能问答系…...

Pixel Couplet Gen实操手册:微信小程序分包加载优化像素春联H5首屏速度

Pixel Couplet Gen实操手册&#xff1a;微信小程序分包加载优化像素春联H5首屏速度 1. 项目背景与核心价值 Pixel Couplet Gen是一款融合传统春节文化与现代像素艺术风格的创新应用。通过ModelScope大模型的文本生成能力&#xff0c;结合精心设计的8-bit视觉元素&#xff0c;…...

【ESP32-S3】通过ROS2使用YDLIDAR X2进行SLAM、自主导航方案选择

通过ROS2使用YDLIDAR X2进行SLAM、自主导航方案选择背景一、方案总览&#xff08;两种主流实现&#xff09;方案A&#xff1a;纯透传&#xff08;最简&#xff0c;推荐入门&#xff09;方案B&#xff1a;Micro-ROS&#xff08;标准ROS 2架构&#xff0c;适合完整导航&#xff0…...

树莓派5新手避坑:用L298N驱动直流电机,从接线到代码的保姆级教程

树莓派5与L298N电机驱动实战&#xff1a;从硬件搭建到PWM调速的深度解析 第一次用树莓派控制直流电机时&#xff0c;我盯着桌上散落的杜邦线和L298N模块&#xff0c;突然意识到自己可能低估了这个看似简单的项目。为什么电机时而抽搐时而静止&#xff1f;为什么PWM调速总是不稳…...

极验点选验证码识别避坑指南:如何应对验证码图片更新带来的挑战

极验点选验证码动态对抗实战&#xff1a;从数据迭代到模型优化的全链路解决方案 当你的验证码识别模型突然失效时&#xff0c;第一反应是什么&#xff1f;上个月刚跑通的极验点选验证码识别系统&#xff0c;在验证码图片更新后准确率从92%暴跌至17%&#xff0c;这是我们团队最近…...

曾经我和大模型交流业务实现记录

第一次&#xff1a; 我有一组子组件11个&#xff0c;通过子组件的不同组合&#xff0c;可以组成表单&#xff0c;这些表单让不同的用户使用&#xff0c;表单组成公共的内容&#xff0c;让大部分用户使用&#xff0c;当然用户可以在这些表单的基础上修改一些默认值&#xff0c;变…...