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

前后端拦截器+MDC实现纯数字 traceId 全链路日志追踪(axios + Spring Boot 超详细实战)

前后端拦截器+MDC实现纯数字 traceId 全链路日志追踪(axios + Spring Boot 超详细实战)

前言

在现代前后端分离的项目中,traceId(追踪 ID) 是定位线上问题、串联日志链路的利器。合理设计和自动化传递 traceId,可以极大提升排查效率和开发体验。本文将手把手教你如何用 axios 和 Spring Boot 的拦截器,在前后端自动生成、传递和打印纯数字 traceId,并结合日志框架+MDC,实现真正的全链路日志追踪。


一、什么是 traceId?为什么要用 traceId?

traceId 是一次请求在系统中流转的唯一标识。无论是前端页面操作、后端接口调用,还是微服务之间的链路追踪,traceId 都能把相关日志串联起来。

主要作用

  • 快速定位问题:出错时只需查 traceId,就能串联起前后端、数据库、第三方服务的所有日志。
  • 方便检索:纯数字 traceId 在日志平台、数据库、监控系统中检索更友好。
  • 开发调试:本地调试、线上排查都能一键定位。

二、技术选型与方案设计

1. 纯数字 traceId 的优势

  • 易于检索:在 Kibana、ELK、数据库等平台,纯数字比字母+符号更好查找。
  • 便于人工输入:有时候需要手动输入 traceId 检索,纯数字更友好。
  • 兼容性好:部分系统或中间件对 header 字段有字符集限制,纯数字更保险。

2. 技术选型

  • 前端:用 nanoid 生成纯数字 traceId,axios 拦截器自动添加到每个请求头。
  • 后端:Spring Boot 拦截器统一获取 traceId,自动打印到日志,必要时生成后端 traceId。
  • 日志框架+MDC:用 logback/log4j2 等日志框架,结合 MDC(Mapped Diagnostic Context),让所有日志自动带上 traceId。

三、前端实现:axios 拦截器自动添加 traceId

1. 安装依赖

npm install axios nanoid
# 或
yarn add axios nanoid

2. 配置 axios 拦截器

// src/utils/traceId.js
import { customAlphabet } from 'nanoid';// 生成16位纯数字 traceId
export const generateTraceId = customAlphabet('0123456789', 16);
// src/utils/axios.js
import axios from 'axios';
import { generateTraceId } from './traceId';// 创建 axios 实例
const instance = axios.create({// baseURL: 'http://your-api-url.com', // 可配置timeout: 10000
});// 请求拦截器:自动添加 traceId
instance.interceptors.request.use(config => {const traceId = generateTraceId();config.headers['traceId'] = traceId;// 也可以在这里打印日志,方便调试console.log(`[traceId=${traceId}] ${config.method?.toUpperCase()} ${config.url}`);// 可选:把 traceId 挂到 config 方便响应拦截器用config.traceId = traceId;return config;
}, error => {return Promise.reject(error);
});// 响应拦截器:可统一处理 traceId 相关逻辑
instance.interceptors.response.use(response => {// 可选:打印响应日志// console.log(`[traceId=${response.config.traceId}] 响应:`, response.data);return response;
}, error => {// 可选:打印错误日志// if (error.config && error.config.traceId) {//   console.error(`[traceId=${error.config.traceId}] 请求出错:`, error);// }return Promise.reject(error);
});export default instance;

3. 使用 axios 实例

// src/api/demo.js
import axios from '../utils/axios';export function getHello() {return axios.get('/api/hello');
}

四、后端实现:Spring Boot 拦截器统一处理 traceId

1. 新建 TraceIdInterceptor

// src/main/java/com/example/demo/interceptor/TraceIdInterceptor.java
package com.example.demo.interceptor;import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Component
public class TraceIdInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String traceId = request.getHeader("traceId");if (traceId == null || traceId.isEmpty()) {traceId = generateServerTraceId();}// 设置到 MDC,所有日志自动带 traceIdMDC.put("traceId", traceId);// 也可以设置到 request attribute,后续 Controller 需要时可用request.setAttribute("traceId", traceId);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {// 请求结束后清理 MDC,防止线程复用污染MDC.remove("traceId");}private String generateServerTraceId() {long timestamp = System.currentTimeMillis();int random = (int)(Math.random() * 1000000);return String.format("%d%06d", timestamp, random);}
}

2.注册拦截器

// src/main/java/com/example/demo/config/WebConfig.java
package com.example.demo.config;import com.example.demo.interceptor.TraceIdInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate TraceIdInterceptor traceIdInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(traceIdInterceptor).addPathPatterns("/**");}}

3.Controller 示例

// src/main/java/com/example/demo/controller/HelloController.java
package com.example.demo.controller;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;@RestController
public class HelloController {private static final Logger logger = LoggerFactory.getLogger(HelloController.class);@GetMapping("/api/hello")public String hello(HttpServletRequest request) {String traceId = (String) request.getAttribute("traceId");// 业务日志logger.info("业务处理逻辑");return "{\"code\":0, \"msg\":\"ok\", \"traceId\":\"" + traceId + "\"}";}}

五、什么是 MDC?为什么要用 MDC?

1. MDC(Mapped Diagnostic Context)简介

MDC(映射诊断上下文)是日志框架(如 logback、log4j2)提供的一种机制,可以为当前线程绑定一些上下文信息(如 traceId、userId、ip 等),这些信息会自动出现在日志输出中。

主要特点
  • 线程隔离:MDC 绑定在当前线程,不会串日志,适合 Web 请求等多线程场景。
  • 自动注入:只需在请求入口(如拦截器)设置一次 traceId,后续所有日志都自动带上 traceId。
  • 日志格式统一:所有日志(包括第三方库、异常栈等)都能自动带上 traceId,方便检索和分析。

2. MDC 的好处

  • 自动化:避免每条日志手动拼接 traceId,减少遗漏和冗余代码。
  • 统一性:日志格式统一,便于团队协作和平台检索。
  • 易于检索:方便后续用 ELK、Kibana、Sentry 等平台检索和分析。
  • 线程安全:MDC 绑定在当前线程,不会出现 traceId 串日志的问题。

3. 为什么不用手动拼接 traceId?

  • 手动拼接容易遗漏,代码冗余。
  • 用 MDC 后,日志格式统一,所有日志都能自动带上 traceId。
  • 只需在请求入口设置一次 traceId,后续所有日志都自动带上。

六、日志框架配置 traceId(以 logback 为例)

1. logback-spring.xml 配置

<!-- src/main/resources/logback-spring.xml -->
<configuration><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><!-- %X{traceId} 就是 MDC 里的 traceId --><pattern>[%X{traceId}] %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern></encoder></appender><root level="INFO"><appender-ref ref="STDOUT"/></root>
</configuration>

2. 日志输出效果

[1234567890123456] 2024-06-11 10:00:00.123 INFO  com.example.demo.controller.HelloController - 业务处理逻辑

七、效果演示

  • 前端所有 axios 请求自动带 traceId
  • 后端所有日志自动带 traceId
  • 全链路追踪,定位问题极其方便

前端控制台示例:

[traceId=1234567890123456] GET /api/hello

后端日志示例:

[1234567890123456] 2024-06-11 10:00:00.123 INFO  com.example.demo.controller.HelloController - 业务处理逻辑

八、常见问题与建议

  1. traceId 长度:建议 12~20 位,太短有碰撞风险,太长不便于人工输入。
  2. 唯一性:nanoid 生成的 ID 唯一性很高,足够绝大多数场景。
  3. 全链路传递:如果有多级服务(如微服务),traceId 要在各服务间传递和打印。
  4. 日志格式统一:建议所有日志都加上 traceId,方便检索。
  5. 前端异常处理:可在 axios 响应拦截器中统一处理 traceId 相关异常日志。
  6. 后端异常处理:建议全局异常处理器也带上 traceId,方便排查。
  7. MDC 清理:一定要在请求结束后清理 MDC,防止线程复用导致 traceId 串日志。

九、流程图

┌──────────────┐
│  用户操作    │
└──────┬───────┘│▼
┌──────────────┐
│ 前端 axios   │
│ 拦截器生成   │
│ 纯数字traceId│
└──────┬───────┘│▼
┌──────────────┐
│ 发送请求     │
│ traceId放header│
└──────┬───────┘│▼
┌──────────────┐
│ 后端拦截器   │
│ 获取/生成    │
│ traceId      │
│ 放入MDC      │
└──────┬───────┘│▼
┌──────────────┐
│ 日志系统     │
│ 自动带traceId│
└──────────────┘

十、总结

  • 前端用 axios 拦截器自动生成并添加 traceId
  • 后端用 Spring Boot 拦截器统一处理 traceId
  • 日志集成 MDC,所有日志自动带 traceId
  • 代码更优雅,日志更统一,排查问题更高效
http://www.lryc.cn/news/571188.html

相关文章:

  • DeepSeek 大型 MoE 模型大规模部署压测学习
  • FlinkCDC-Hudi数据实时入湖原理篇
  • JVM监控的挑战:Applications Manager如何提供帮助
  • Spring Boot集成Kafka全攻略:从基础配置到高级实践
  • 多模态大语言模型演进:从视觉理解到具身智能的技术突破
  • Linux运维新人自用笔记(部署 ​​LAMP:Linux + Apache + MySQL + PHP、部署discuz论坛)
  • 5.安装IK分词器
  • ELK在Java的使用
  • Selenium(选择元素,浏览器/元素操作,等待,页面交互)
  • Windows Python 环境管理终极对比:极简方案 VS 传统方案(仅需 2 个软件实现全流程自动化)
  • Selenium(多窗口,frame,验证码,截图,PO模式)
  • rockx读取单张图片并检测图片内人脸的矩形
  • vite的常用配置
  • 「动态规划::数位DP」统计数字递推 / LeetCode 3352|1012(C++)
  • 线程池(Thread Pool)详解
  • 基于Cesium移动的天空云
  • 【Docker基础】Docker核心概念:命名空间(Namespace)之IPC详解
  • 根据Python模块的完整路径import动态导入
  • 05_MinIO+Java SpringBoot 实现透传代理下载
  • 如何确定驱动480x320分辨率的显示屏所需的MCU主频
  • 为何前馈3DGS的边界总是“一碰就碎”?PM-Loss用“3D几何先验”来解
  • Mac 安装JD-GUI
  • 低轨导航 | 低轨卫星导航PNT模型,原理,公式,matlab代码
  • 软件工程:流程图如何画?
  • Python 爬虫入门 Day 5 - 使用 XPath 进行网页解析(lxml + XPath)
  • springboot使用kafka
  • Jmeter的三种参数化方式详解
  • web前端开发核心基础:Html结构分析,head,body,不同标签的作用
  • Java内存模型与线程
  • Anaconda 使用