TraceId 介绍
TraceId (链路追踪 ID) 是贯穿一次完整请求生命周期的唯一标识。
一般用于分布式系统排查、监控、定位问题。从网关/入口服务开始生成,一路穿透传给所有下游服务、中间件、日志、数据库等。
本文目标
本文只针对单个 Spring Boot 工程实现 TraceId 生成。用于单体服务,多请求并发时精准识别某个请求的日志。
- 使用 hutool 实现唯一 ID
- 基于 MDC 实现日志记录并重写 logback 默认日志输出格式,添加 traceId 输出
- 返回 Response 头 X-Trace-Id
- 支持多线程
代码实现
定义 Interceptor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
import cn.hutool.core.util.IdUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@RequiredArgsConstructor
@Component
public class TraceIdInterceptor implements HandlerInterceptor {
public static final String TRACE_ID = "traceId";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String tracedId = IdUtil.fastSimpleUUID();
MDC.put(TRACE_ID, tracedId);
response.setHeader("X-Trace-Id", tracedId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
MDC.remove(TRACE_ID);
}
}
|
注册 Interceptor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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 TraceIdConfig implements WebMvcConfigurer {
private TraceIdInterceptor traceIdInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(traceIdInterceptor)
.addPathPatterns("/**");
}
@Autowired
public void setTraceIdInterceptor(TraceIdInterceptor traceIdInterceptor) {
this.traceIdInterceptor = traceIdInterceptor;
}
}
|
多线程支持
通过 TaskDecorator 实现多线程支持,将 MDC 中的 traceId 传递给子线程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import org.slf4j.MDC;
import org.springframework.core.task.TaskDecorator;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
public class MdcTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
Map<String, String> context = MDC.getCopyOfContextMap();
return () -> {
try {
if (context != null) MDC.setContextMap(context);
runnable.run();
} finally {
MDC.clear();
}
};
}
}
|
如上,即可在 @Async 的方法中日志显示 traceId。
日志格式修改
在 logback 中使用 %X{traceId} 来获取 traceId 内容。作者使用如下 pattern (供参考):
1
|
[%X{traceId}] %d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${PID:- } --- [%15.15t] %-40.40logger{39} : %m%n
|
logback 完整配置可参考 logback.xml 文件解析