✅ 需求
如果大家希望在 每次外部请求进来时,日志中都能带上一个 指定标识(例如:requestId 或 traceId),以便后续排查问题、链路追踪或日志聚合分析。
✅ 目标
每次 HTTP 请求进来后,在整个调用链路中,所有通过 log.info()
、log.error()
等输出的日志都自动带上一个唯一标识(如 requestId
),便于追踪请求路径。
✅ 实现方式概述
你可以使用 MDC(Mapped Diagnostic Context) 来实现这个功能。MDC 是 log4j、logback 等日志框架提供的线程上下文机制,可以将一些变量绑定到当前线程中,并在日志模板中引用这些变量。
🔧 改造步骤如下:
🧱 一、修改日志格式模板(log4j.properties / logback.xml)
你需要确保日志输出格式中包含 MDC 的字段,比如 %X{requestId}
。
如果你使用的是 log4j.properties:
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c - %X{requestId} %m%n
如果你使用的是 logback.xml
:
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %X{requestId} %msg%n</pattern>
这样配置之后,只要你在代码中设置了 MDC.put("requestId", "xxx")
,该值就会自动出现在每条日志中。
🧱 二、在请求进入系统前设置 requestId(推荐使用拦截器)
✅ 推荐做法:创建一个 Spring 拦截器(Interceptor)
1. 创建拦截器类 RequestLogInterceptor.java
import org.slf4j.MDC;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
public class RequestLogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 生成唯一的 requestId
String requestId = UUID.randomUUID().toString().replace("-", "");
// 将 requestId 存入 MDC 上下文
MDC.put("requestId", requestId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 请求结束后清理 MDC,避免内存泄漏
MDC.clear();
}
}
🧱 三、注册拦截器
在 Spring MVC 配置文件中添加拦截器配置:
<mvc:interceptors>
<bean class="com.yourpackage.RequestLogInterceptor"/>
</mvc:interceptors>
或者使用 Java Config 方式:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestLogInterceptor());
}
}
🧱 四、验证效果
假设你有如下日志输出:
log.info("处理用户登录请求");
改造后日志会变成:
2024-10-18 14:23:45 [http-nio-8080-exec-1] INFO com.example.LoginController - abcdef123456789 处理用户登录请求
其中 abcdef123456789
就是本次请求的 requestId
。
🧱 五、可选增强:结合 NDC 做更复杂的链路追踪(可选)
如果你要做更完整的分布式链路追踪(TraceId + SpanId),可以考虑引入:
- Spring Cloud Sleuth + Zipkin
- SkyWalking
- Pinpoint
但如果是单体项目或轻量级改造,使用上面的 MDC 已经足够。
✅ 总结
步骤 | 内容 |
1️⃣ | 修改日志格式,加入 |
2️⃣ | 创建拦截器,在请求开始时设置 |
3️⃣ | 请求结束时清理 MDC,防止线程复用导致串号 |
4️⃣ | 验证日志是否带上了 requestId |
💡 补充建议
- 可以将
requestId
返回给前端(放在响应头中),便于前后端联调。 - 使用 UUID 以外,也可以用时间戳+随机数组合生成 requestId。
- 如果你想让 SQL 日志也带上 requestId,可在
DatabaseUtil
中打印 SQL 时也使用MDC.get("requestId")
。