日志全链路跟踪的一种实现

文章介绍了如何通过在Web服务和Dubbo服务中生成并传递全局唯一的TraceId来实现调用链路的日志连贯性。主要方法包括在Web服务中使用拦截器或AOP,在Dubbo服务中添加过滤器,确保TraceId在请求过程中被正确地设置和传递。同时,通过MDC管理TraceId,并在日志格式中包含它,以便于跟踪整个链路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注:记录开发,自己总结,随便写写,不喜勿喷。

问题描述

之前有聊过怎么去在web服务与dubbo服务记录调用日志。

web服务:https://blog.youkuaiyun.com/weixin_42459814/article/details/128961326

dubbo服务:https://blog.youkuaiyun.com/weixin_42459814/article/details/128960789

那问题来了,怎么把整个链路的日志连起来呢?不可能看参数吧?

解决方案

一般会考虑程序入口生成一个全局唯一的ID,也就是分布式唯一ID,然后在整个调用链传递下去,那具体怎么做呢?(分布式id生成这里不讲哈)

如在tomcat服务中加个拦截器(或者aop),如下:

@Aspect
@Component
public class Aspect {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);
 
    @Pointcut("@annotation(esa.restlight.spring.shaded.org.springframework.web.bind.annotation.RequestMapping)" +
            " || @annotation(esa.restlight.spring.shaded.org.springframework.web.bind.annotation.PostMapping)" +
            " || @annotation(esa.restlight.spring.shaded.org.springframework.web.bind.annotation.GetMapping)"
    )
    public void controllerMethods() {
        // Do nothing
    }
 
    @Around("controllerMethods()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        AsyncRequest request = RequestResponseHolder.getRequest();
        long start = System.currentTimeMillis();
        //request中取或者生成traceId存入MDC,看自己怎么生成
        String traceId = getTraceId();
        MDC.put("traceId", traceId);
        LOGGER.info("<<==request:{},param {} ==>>", request.path(), pjp.getArgs());
        Object result = pjp.proceed();

        LOGGER.info("<<==response:{},param {},result ,{} ,time,{} ==>>",
                    request.path(), pjp.getArgs(), result, System.currentTimeMillis() - start);
        //这里要删哦,MDC用的ThreadLocal
        MDC.remove("traceId");
        return result;
    }
}

公共包给Dubbo服务增加filter

@Activate(
        group = {Constants.CONSUMER}
)
public class MDCLogConsumerFilter implements Filter {
 
    private static final Logger logger = LoggerFactory.getLogger(RequestContextConsumerFilter.class);
     
    @Override public Result invoke(
            Invoker<?> invoker, Invocation invocation) throws RpcException {
 
        String traceId = MDC.get("traceId");
        logger.debug("trace id : {}", traceId);
 
        try {
            //没有traceId,要去创建一个哦
            if (StringUtils.isEmpty(traceId)) {
                traceId = getTraceId();
            }
            RpcInvocation rpcInvocation = (RpcInvocation)invocation;
            rpcInvocation.setAttachment("traceId", traceId);
            
        } catch (Throwable throwable) {
            logger.warn("设置traceId 失败", throwable);
        }
 
        return invoker.invoke(invocation);
    }
}

@Activate(
        group = {Constants.PROVIDER}
)
public class MDCLogProviderFilter implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(MDCLogProviderFilter.class);
  
    @Override public Result invoke(
            Invoker<?> invoker, Invocation invocation) throws RpcException {

        String traceId = invocation.getAttachment("traceId");

        logger.debug("trace id : {}", traceId);

        if (StringUtils.isEmpty(traceId)) {
            traceId = UUID.randomUUID().toString().replace("-", "");
            logger.debug("获取traceId失败,生成新的traceId : {}", traceId);
        }

        Result result = null;
        try {
            MDC.put(KEY, traceId);
            result = invoker.invoke(invocation);
        } finally {
            MDC.remove(KEY);
        }

        return result;
    }
}

正常情况下,这样就在整个链路都有同一个traceId了,那我们在配置日志打印格式的时候,把traceId打印进去就好了,如下:

%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-[%traceId] %-5level %logger{36} - %msg%n

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值