微服务透传日志traceId

问题

在微服务架构中,一次业务执行完可能需要跨多个服务,这个时候,我们想看到业务完整的日志信息,就要从各个服务中获取,即便是使用了ELK把日志收集到一起,但如果不做处理,也是无法完整把一次业务请求的日志完整链路串联起来。有人说,可以在日志中加入某个业务参数,比如订单id等,但是不可能所有业务都是有这样的参数给你用的。

解决方案

在打印日志的时候加入一个可以贯穿整个业务请求的唯一标识,我们把它称traceId。下面是在一个物联网项目的微服务架构中,使用dubbo时的基本流程:

拿到traceId,该怎么使用呢?答案也很简单,用过logback的都知道,是可以在日志信息里配置一些公共参数的,比如方法名,类名,线程名等,我们把traceId也设置进去就可以了。

logback配置如下:

xml 复制代码
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%date{yyyy-MM-dd HH:mm:ss} [%-5level]  [%X{traceId}]  [%thread] [%logger{80}] [%file:%line] [%method]>>>>>> %msg%n</pattern>
            <charset>utf-8</charset>
        </encoder>

代码中设置:

web过滤器,针对的是HTTP请求,

java 复制代码
@Configuration
public class TraceFilter extends OncePerRequestFilter {


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws IOException, ServletException {
        try {
            String uuid = UUID.randomUUID().toString();
            MDC.put("traceId",uuid);
            filterChain.doFilter(request, response);
        } finally {
            // 必须清除
            MDC.clear();
        }
    }
}

跨服务调用走dubbo:

消费者过滤器:

java 复制代码
@Activate(group = {CommonConstants.CONSUMER})
public class ConsumerDubboFilter implements org.apache.dubbo.rpc.Filter{

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        Map<String, Object> attachments = invocation.getObjectAttachments();
        // 获取traceId
        String traceId = MDC.get("traceId");
        // 传递日志追踪信息
        attachments.put("traceId",traceId);
        return invoker.invoke(invocation);
    }
}

生产者过滤器:

java 复制代码
@Activate(group = {CommonConstants.PROVIDER})
public class ProviderDubboFilter implements org.apache.dubbo.rpc.Filter{

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        try {
            // 获取到traceId
            String traceId = (String)invocation.getObjectAttachment("traceId");
            // 设置traceId
            MDC.put("traceId",traceId);
            return invoker.invoke(invocation);
        }finally {
            // 必须清除
            MDC.clear();
        }
    }
}

有聪明的小伙伴,此时已经想到了一个问题,那就是如果在子线程中打印日志依然是拿不到traceId的,没关系,照样可以解决,子线程传递上下文信息,第一个想到的是什么?对,阿里的TTL,

在pom中引入:

xml 复制代码
        <dependency>
            <groupId>com.ofpay</groupId>
            <artifactId>logback-mdc-ttl</artifactId>
            <version>1.0.2</version>
        </dependency>

logback的xml配置中加入一行代码:

xml 复制代码
<contextListener class="com.ofpay.logback.TtlMdcListener"/>

还有伙伴会问,如果用的是OpenFeign呢,其实原理一样,使用OpenFeign的拦截器,在跨服务调用之前拦截,拿到traceId,再放入HTTP请求头中,下游服务在过滤器中再从请求头中获取即可。这里代码就不再展示,直接搜feign的拦截器使用就行了。

扩展

既然traceId可以这样透传,那么其他的一些公共的信息当然也可以透传,比如分页信息,用户信息,ThreadLocal中上下文信息,都可以使用这样的方式去透传,让开发人员感觉还是在单服务中一样简便。

下一篇:MQTT+Disruptor提高并发

相关推荐
咖啡八杯29 分钟前
GoF设计模式——中介者模式
java·后端·spring·设计模式
fanly111 小时前
Surging AI Agent 完整产品介绍
微服务·microservice
青石路4 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
Java陈序员6 小时前
企业级!一个基于 Java 开发的开源 AI 应用开发平台!
spring boot·agent·mcp
杉氧7 小时前
深入理解 Compose 重组机制:快照系统如何驱动 UI 精准刷新?
android·架构·android jetpack
像我这样帅的人丶你还7 小时前
Java 后端详解(五):Redis 缓存
java·后端·全栈
杉氧7 小时前
深度解析:Jetpack Compose 核心架构与底层原理 —— 十年安卓老兵的“破茧重生”
android·架构·android jetpack
Lion098 小时前
ReAct 循环:Agent 的思考引擎 — Think → Act → Observe
架构
阿里云云原生8 小时前
Higress v2.2.3 发布:正式入驻 CNCF Sandbox,AI Gateway 与 Ingress 迁移能力双向加固
云原生
plainGeekDev9 小时前
GreenDAO → Room
android·java·kotlin