SpringBoot中使用TraceId进行日志追踪

SpringBoot中TraceId实现日志追踪

**查询日志的痛点:**项目中每当我们查询日志的时候都是看前端请求什么接口,根据一些关键字进入服务器查询日志中是否有这个关键字,然而这个关键字在日志里面并不是唯一的,所以要生成一个唯一的标识,每一次请求都是唯一的一串字符,查询会过滤掉很多无用的信息,快捷查找到这次请求。为了解决这个痛点,就使用了TraceId。

一、TraceId 定义

用于标识某一次具体的请求ID。当用户的请求进入系统后,会在RPC调用网络的第一层生成一个全局唯一的traceId,并且会随着每一层的RPC调用,不断往后传递,这样的话通过traceId就可以把一次用户请求在系统中调用的路径串联起来。

在分布式系统中,一个请求可能会涉及多个服务和组件的调用,而traceId可以帮助我们追踪和查看整个请求的流程和调用链

二、TraceId的请求流程

在最开始请求系统时候生成一个全局唯一的traceId,放在http 请求header中,系统接收到请求后,从header中取出这个traceId,放入MDC中,这个traceId伴随着这整个请求的调用周期,即当一个服务调用另外一个服务的时候,需要将traceId往下传递,从而形成一条链路。

注意:这次的traceId主要是用UUID来生成的,前端生成traceId,后端拦截器拦截到了做一层判断,如果前端有,就不需要后端生成,而且只需要将前端的这个traceId发给后端人员,后端人员就能直接能定位到日志,不需要再让前端给请求的是什么接口,所以建议是前端来生成traceId。

三、SpringBoot整合TraceId

(一)添加一个logback.xml

logback.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="LOG_PATH" value="/xxx/log"/>  <!-- 项目的日志路径 -->
    <property name="LOG_FILE" value="xxx"/>
    <property name="DATE_PATH" value="${LOG_PATH}/%d{yyyy-MM-dd}"/>

    <!-- 日志输出格式 -->
    <property name="log.pattern.console"
              value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) [traceId:%X{traceId}] %highlight(%-5level) %boldMagenta(%logger{10}) - %cyan(%msg%n)"/>
  
在多个单体 Spring Boot 项目中实现 `traceId` 并将其输出到日志中,可以通过以下方式完成: ### 3.1 集成日志框架 Spring Boot 支持多种日志框架,如 Logback、Log4j2 等。推荐使用 Logback,它是 Spring Boot 默认的日志实现。为了支持 `traceId`,需要在日志配置中使用 MDC(Mapped Diagnostic Context),它允许在每个日志行中附加上下文信息[^1]。 ### 3.15 集成日志框架 [^1] 在 `application.yml` 或 `application.properties` 中配置日志格式,确保 `traceId` 被包含在日志输出中。例如,在 `logback-spring.xml` 中定义日志模式: ```xml <configuration> <appender name="STDOUT" 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="STDOUT" /> </root> </configuration> ``` 在这个配置中,`%X{traceId}` 用于从 MDC 中提取 `traceId` 并将其写入日志。 ### 3.11 集成定时任务 为了确保每个请求都有唯一的 `traceId`,可以在请求进入应用时生成一个唯一的 ID,并将其存储在 MDC 中。这通常通过拦截器或过滤器实现: ```java 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; import java.util.UUID; @Component public class TraceInterceptor implements HandlerInterceptor { private static final String TRACE_ID = "traceId"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String traceId = UUID.randomUUID().toString(); MDC.put(TRACE_ID, traceId); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { MDC.clear(); } } ``` 通过 `HandlerInterceptor`,在请求开始时生成 `traceId` 并放入 MDC,在请求结束时清除 MDC,防止线程复用导致的数据污染。 ### 3.18 单元测试 为了验证 `traceId` 是否正确注入到日志中,可以编写单元测试来模拟请求并检查日志输出: ```java import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; @SpringBootTest @AutoConfigureMockMvc public class TraceIdTest { private static final Logger logger = LoggerFactory.getLogger(TraceIdTest.class); @Autowired private MockMvc mockMvc; @Test public void testTraceIdInLogs() throws Exception { logger.info("This log should contain a traceId"); // 可以结合日志捕获工具(如 LogCaptor)验证 traceId 是否存在 } } ``` ### 3.19 集成监控中心 若多个 Spring Boot 项目部署在不同的服务中,可以结合分布式追踪工具(如 Zipkin、Sleuth)来实现跨服务的 `traceId` 传播。Spring Cloud Sleuth 可以自动为每个请求生成 `traceId` 并将其传播到下游服务,便于集中日志分析和分布式追踪。 添加 Sleuth 依赖后,`traceId` 将自动注入到日志中: ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency> ``` ### 3.20 集成单元测试框架 若使用日志捕获工具(如 LogCaptor),可在单元测试中验证日志中是否包含 `traceId`: ```java import com.github.valfirst.slf4j.test.LoggingEvent; import com.github.valfirst.slf4j.test.TestLoggerFactory; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; import java.util.List; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @AutoConfigureMockMvc public class TraceIdLoggingTest { @Autowired private MockMvc mockMvc; @Test public void testTraceIdInLogs() throws Exception { Logger logger = TestLoggerFactory.getTestLogger(YourController.class); mockMvc.perform(get("/api/test")).andExpect(status().isOk()); List<LoggingEvent> events = TestLoggerFactory.getLoggingEvents(YourController.class); for (LoggingEvent event : events) { // 验证日志中是否包含 traceId assert event.getMessage().contains("traceId="); } } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值