基于traceId解决日志的请求调用问题

背景

情况一

在微服务项目中,我们使用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();
            }
        };
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值