背景
情况一
在微服务项目中,我们使用openfeign这些来远程调用别的服务的方法的时候,如果遇到问题,比如说想去查一个请求下的日志,是没有办法做到的,我们可以在日志里面添加一个唯一标识,比如雪花算法这些来解决问题
情况二
我们想知道一个请求中所有和该请求相关的链路日志,此时也需要为日志增加一个唯一标识。通常可以使用UUID或者其它雪花算法等作为唯一标识;基于以上场景,我们可以为每个请求设置一个traceTd,这个请求整个链路公用同一个traceTd,然后将日志收集到统一日志平台,
通过日志关键字找出traceId,再根据traceTd,找出整个链路的请求过程,甚至还可以与分布式链路框架skywalking结合,分析链路的性能。
实现方案
1.如何实现
利用MDC, MDC(Mapped Diagnostic Context)映射诊断环境,是log4j和logback提供的一种方便在多线程条件下记录日志的功能,可以看成是一个与当前线程绑定的ThreadLocal,可以往其中添加键值对。
MDC的使用方法
- MDC中设置值:MDC.put(key, value);
- MDC中取值:MDC.get(key);
- MDC中内容打印到日志中:%X{traceId}
2.实现步骤
2.1 封装traceId和spanId相关的sdk
2.2在日志配置文件logback-spring.xml中配置traceId和spanId
<property name="CONSOLE_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level[${APP_NANE},%thread,%X{X-B3-TraceId:-},%X(X-B3-SpanId:-)]%logger{50}.%method:%L- %m%n"/>
2.3 spring cloud gateway中记录traceId,在过滤器SecurityFilter中实现
2.4 spring cloud gateway-----> 微服务中记录traceId,提供TraceWebFilter过滤器实现
2.5微服务多线程调用traceId传递,通过创建threadPoolTaskExecutor的任务适配器或自定义threadPoolTaskExecutor
https://www.csdn.net/tags/OtDaIg1sMTMxODEtYmxvZwO0O0OO0O0O.html
2.6微服务----> 微服务之间traceId传递,服务之间是通过feign调用,我们通过实现RequestInterceptor拦载器实现traceId传递
https://blog.csdn.net/qq_42145871/article/details/108824056
https://www.cnblogs.com/wlandwl/p/trace.html
具体的代码
SDK
@Data
public class Trace implements Serializable {
private static final long SerialVersionID = 1L;
public static final String TRACE = "X-B3-TraceId";
public static final String PARENT_SPAN="X-B3-SpanId";
private String traceId;
private String spanId;
}
public class DCMd5Util extends MD5 {
public static String getTraceId(String source){
return SecureUtil.md5(source).substring(2,16);
}
}
public class TraceUntil {
public final static ThreadLocal<Trace> CURRENT_SPAN = new NamedThreadLocal<>("TraceId Context");
public static String genTraceId(){
return DCMd5Util.getTraceId(UUID.randomUUID().toString());
}
public static String genSpanId(){
return Convert.toStr(new SnowflakeGenerator().next());
}
//设置trace信息
public static void setCurrentTrace(String traceId){
if (Strings.isBlank(traceId)){
traceId = genTraceId();
}
Trace trace = new Trace();
trace.setTraceId(traceId);
trace.setSpanId(genSpanId());
MDC.put(Trace.TRACE,trace.getTraceId());
MDC.put(Trace.PARENT_SPAN,trace.getSpanId());
CURRENT_SPAN.set(trace);
}
//得到trace信息
public static Trace getCurrentTrace(){
Trace trace = CURRENT_SPAN.get();
if (trace==null){
trace = new Trace();
trace.setTraceId(genTraceId());
trace.setSpanId(genSpanId());
MDC.put(Trace.TRACE,trace.getTraceId());
MDC.put(Trace.PARENT_SPAN,trace.getSpanId());
CURRENT_SPAN.set(trace);
}else {
trace.setSpanId(genSpanId());
MDC.put(Trace.PARENT_SPAN,trace.getSpanId());
CURRENT_SPAN.set(trace);
}
return trace;
}
//清空traceId
public static void clearCurrentTraceId( ){
CURRENT_SPAN.set(null);
CURRENT_SPAN.remove();
}
}
2.5微服务多线程调用traceId传递,通过创建threadPoolTaskExecutor的任务适配器或自定义threadPoolTaskExecutor
https://blog.csdn.net/songhongjin/article/details/107780984
public class MdcTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
return ()->{
try {
MDC.setContextMap(copyOfContextMap);
MDC.put(Trace.PARENT_SPAN,TraceUntil.genSpanId());
runnable.run();
}finally {
MDC.clear();
}
};
}
}