**查询日志的痛点:**项目中每当我们查询日志的时候都是看前端请求什么接口,根据一些关键字进入服务器查询日志中是否有这个关键字,然而这个关键字在日志里面并不是唯一的,所以要生成一个唯一的标识,每一次请求都是唯一的一串字符,查询会过滤掉很多无用的信息,快捷查找到这次请求。为了解决这个痛点,就使用了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)"/><property name="log.pattern.file"value="%d{HH:mm:ss.SSS} [%thread] [traceId:%X{traceId}] %-5level %logger{20} - [%method,%line] - %msg%n"/><include resource="org/springframework/boot/logging/logback/defaults.xml"/><include resource="org/springframework/boot/logging/logback/console-appender.xml"/><!-- 控制台输出 --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${log.pattern.console}</pattern><charset>utf8</charset></encoder></appender><!-- 日志文件fzwyapi-admin.log --><appender name="fileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${LOG_PATH}/${LOG_FILE}.log</file><append>true</append><encoder>
<!-- <pattern>${CONSOLE_LOG_PATTERN}</pattern>--><pattern>${log.pattern.file}</pattern></encoder><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><fileNamePattern>${DATE_PATH}/%i.log</fileNamePattern><maxHistory>15</maxHistory><totalSizeCap>20GB</totalSizeCap><maxFileSize>50MB</maxFileSize></rollingPolicy></appender><appender name="fileError" class="ch.qos.logback.core.rolling.RollingFileAppender"><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>ERROR</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter><file>${LOG_PATH}/${LOG_FILE}.error.log</file><append>true</append><encoder><!-- <pattern>${CONSOLE_LOG_PATTERN}</pattern>--><pattern>${log.pattern.file}</pattern></encoder><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><fileNamePattern>${DATE_PATH}/%i.error.log</fileNamePattern><maxHistory>15</maxHistory><totalSizeCap>20GB</totalSizeCap><maxFileSize>50MB</maxFileSize></rollingPolicy></appender><root level="info"><appender-ref ref="fileAppender"/><appender-ref ref="fileError"/><appender-ref ref="STDOUT"/></root></configuration>
(二)添加TraceId工具类
TraceIdUtil.java
package xxx;import org.slf4j.MDC;import java.util.UUID;/*** @author lyc* @date 2024/10/17* @funtion*/
public class TraceIdUtil {public static final String TRACE_ID = "traceId";public static String getTraceId() {String traceId = MDC.get(TRACE_ID);return traceId == null ? "" : traceId;}public static void setTraceId(String traceId) {MDC.put(TRACE_ID, traceId);}public static void remove() {MDC.remove(TRACE_ID);}public static void clear() {MDC.clear();}public static String generateTraceId() {return UUID.randomUUID().toString().replace("-", "");}
}
(三)添加TraceId拦截器
SystemTraceIdInterceptor.java
package xxx;import xxx.TraceIdUtil;
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;/*** @author lyc* @date 2024/10/17* @funtion*/
@Component
public class SystemTraceIdInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {//如果有上层调用就用上层的IDString traceId = request.getHeader(TraceIdUtil.TRACE_ID);if (traceId == null) {traceId = TraceIdUtil.generateTraceId();}MDC.put(TraceIdUtil.TRACE_ID, traceId);//配置traceId到响应header中response.setHeader(TraceIdUtil.TRACE_ID, MDC.get(TraceIdUtil.TRACE_ID));return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {//调用结束后删除MDC.remove(TraceIdUtil.TRACE_ID);}
}
(四)InterceptorConfig配置TraceId拦截器
InterceptorConfig.java
package xxx.config;import xxx.SystemTraceIdInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;/*** @author lyc* @updated: 2024/10/17* @funtion*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Resourceprivate SystemTraceIdInterceptor systemTraceIdInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//配置链路追踪traceId拦截器registry.addInterceptor(systemTraceIdInterceptor).addPathPatterns("/**");}}
四、效果