1.创建全局过滤器,在请求头上带入traceId参数,穿透到下游服务.
package com.by.filter;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.jwt.JWTValidator;
import cn.hutool.jwt.signers.JWTSignerUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.util.List;
@Slf4j
@Component
public class TraceIdFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
List<String> traceIds = request.getHeaders().get("traceId");
//判断是否有traceId
if (CollUtil.isNotEmpty(traceIds)) {
// 放行
return chain.filter(exchange);
}
//为空,生成一个traceId
String traceId = IdUtil.simpleUUID();
// 创建新的请求
ServerHttpRequest newRequest = request.mutate().header("traceId", traceId).build();
// 创建新的exchange
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
// 放行
return chain.filter(newExchange);
}
}
2.MDC原理
当请求来时生成一个traceId放在ThreadLocal里,然后打印时去取就行了。但在不改动原有输出语句的前提下自然需要日志框架的支持了。
MDC 介绍 MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的Map,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。
3.下游服务如何使用全链路跟踪Id
3.1配置TraceId 过滤器
package com.by.filter;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
//@Order(1)//优先级
@WebFilter//过滤器放入IOC
public class TraceIdFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;//转换 类型
String traceId = httpServletRequest.getHeader("traceId");//获取请求头
//将traceId放入MDC
MDC.put("traceId", traceId);
try {
//放行
chain.doFilter(request, response);
}finally {
//清除
MDC.clear();
}
}
}
3.2 启动类开启ServletComponentScan扫描。
@SpringBootApplication
@ServletComponentScan
public class OpenApp {
public static void main(String[] args) {
SpringApplication.run(OpenApp.class, args);
}
}
3.3 配置文件配置日志输出格式
##日志输出格式
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} %clr(%-5level) %clr([%X{traceId}]) %clr(${PID:-}) --- %clr(%logger{50}) - %m%n
4.Openfeign扩展
@Component
public class OpenFeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
String traceId = MDC.get(TraceIdFilter.MDC_TRACE_ID);
requestTemplate.header(TraceIdFilter.MDC_TRACE_ID, traceId);
}
}