如何在 Logback 日志框架中加入链路 ID
在 Logback 日志框架中加入链路 ID,能有效将同一条链路的日志串联起来,便于追踪和排查问题。
1. 生成和管理链路 ID
要保证在整个请求链路里都能获取到链路 ID,可借助 ThreadLocal
来实现。以下是一个简单的工具类示例:
import java.util.UUID;public class TraceIdUtil {private static final ThreadLocal<String> traceIdHolder = new ThreadLocal<>();// 生成链路 IDpublic static String generateTraceId() {String traceId = UUID.randomUUID().toString();traceIdHolder.set(traceId);return traceId;}// 获取链路 IDpublic static String getTraceId() {return traceIdHolder.get();}// 清除链路 IDpublic static void clearTraceId() {traceIdHolder.remove();}
}
2. 在请求入口生成链路 ID
可使用拦截器、过滤器或者 AOP 来在请求进入时生成链路 ID。这里以拦截器为例:
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class TraceIdInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 生成链路 IDString traceId = TraceIdUtil.generateTraceId();// 可将链路 ID 放入响应头,方便下游服务获取response.setHeader("Trace-Id", traceId);return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// 请求处理完成后清除链路 IDTraceIdUtil.clearTraceId();}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 请求完成后再次清除链路 ID,确保不会有残留TraceIdUtil.clearTraceId();}
}
同时,要在 Spring Boot 中配置该拦截器:
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 {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new TraceIdInterceptor()).addPathPatterns("/**"); // 拦截所有请求}
}
3. 配置 Logback 日志格式
在 logback.xml
配置文件里,使用 %X{traceId}
来获取并展示链路 ID。示例配置如下:
<configuration><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><!-- 配置日志输出格式,加入链路 ID --><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] %-5level %logger{36} - %msg%n</pattern></encoder></appender><root level="info"><appender-ref ref="CONSOLE" /></root>
</configuration>
在上述配置中,%X{traceId}
用于输出存储在 MDC(Mapped Diagnostic Context)中的链路 ID。
4. 在代码中使用 MDC 存储链路 ID
在生成链路 ID 后,要把它存储到 MDC 中,这样 Logback 才能正确获取。可在拦截器中添加如下代码:
import org.slf4j.MDC;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class TraceIdInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 生成链路 IDString traceId = TraceIdUtil.generateTraceId();// 将链路 ID 存入 MDCMDC.put("traceId", traceId);// 可将链路 ID 放入响应头,方便下游服务获取response.setHeader("Trace-Id", traceId);return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// 请求处理完成后清除 MDC 中的链路 IDMDC.remove("traceId");// 请求处理完成后清除链路 IDTraceIdUtil.clearTraceId();}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 请求完成后再次清除 MDC 中的链路 ID,确保不会有残留MDC.remove("traceId");// 请求完成后再次清除链路 ID,确保不会有残留TraceIdUtil.clearTraceId();}
}
通过以上步骤,就能在 Logback 日志框架中加入链路 ID,使每条日志都包含对应的链路 ID,方便进行链路追踪和问题排查。