当前位置: 首页 > 科技观察

微服务分布式架构中如何实现日志链接跟踪?

时间:2023-03-16 16:32:47 科技观察

背??景开发中解决系统问题最常用的方法就是查看系统日志。在分布式环境中,一般使用ELK来统一收集日志,但是在并发量大的时候使用日志来定位问题还是比较麻烦。我们看下图:上图中,一个用户请求一个url,整个链接如图所示,每个处理层都会产生日志,那么我们如何将这些日志串起来形成一个请求全路径日志.在现有系统中,由于大量其他用户/其他线程的日志是一起输出的,因此很难过滤掉某个特定请求的所有相关日志。那我们怎么处理呢?作为解决方案,我们可以让每个请求都有一个唯一的标识,然后我们可以在打印日志的时候为每个请求使用一个唯一的标识,而这个唯一标识需要传递给下游服务,下游服务在打印日志的时候,它还带有此唯一标识符,以便可以跟踪所有链接并将其显示在日志中。技术实施方案是什么?我们应该尽量避免侵入代码,利用Logback的MDC机制在日志模板中加入traceId标识,取值方式为%X{traceId}。什么是MDCMDC(MappedDiagnosticContext,MappedDebuggingContext)是log4j和logback提供的一个方便多线程情况下日志记录的功能。MDC可以看成是绑定在当前线程上的一个Map,可以在上面添加键值对。MDC中包含的内容可以由在同一线程中执行的代码访问。当前线程的子线程会继承其父线程中MDC的内容。当需要日志记录时,仅从MDC获取所需的信息。MDC的内容由程序在适当的时候保存。对于Web应用程序,此数据通常在处理请求的最开始时保存。方案实现因为MDC内部使用了ThreadLocal,只有本线程有效,子线程和下游服务MDC中的值都会丢失;因此,该方案的主要难点在于解决价值传递的问题,主要包括以下几个部分:API网关如何将下游服务中的MDC数据传递给下游服务如何接收数据,如何继续调用其他远程服务时传递数据(线程池)如何传递给子线程修改日志模板logback配置文件日志格式添加这个标识gateway添加filter这个filter是解决gateway如何将MDC数据传递给下游服务:生成一个traceId,通过header传递给下游服务。上面代码中有一个属于org.slf4j.MDC的MDC,下面是常量的值:/***Loglinktrackingidinformationheader*/StringTRACE_ID_HEADER="x-traceId-header";/***日志链接跟踪id日志标志*/StringLOG_TRACE_ID="traceId";下游服务添加spring拦截器接收并保存traceId值:下游服务添加feign拦截器继续将当前服务的traceId值传递给下游服务:解决父子线程传递问题主要针对业务将使用线程池(异步,并行处理),而spring本身也有@Async注解使用线程池,解决这个问题需要以下两步:重写logback的LogbackMDCAdapter,因为logback的MDC内部实现使用了ThreadLocal,不能传子threads,所以需要重写,换成阿里的TransmittableThreadLocal。TransmittableThreadLocal是阿里巴巴开源的InheritableThreadLocal扩展,用于解决“使用线程池等缓存线程的组件时传输ThreadLocal”的问题。如果要在线程池和主线程之间传递TransmittableThreadLocal,需要配合TtlRunnable和TtlCallable使用。其余代码与ch.qos.logback.classic.util.LogbackMDCAdapter相同,只是调用copyOnInheritThreadLocal变量。TtlMDCAdapterInitializer类用于程序启动时加载自己的mdcAdapter实现:扩展线程池实现增加TtlRunnable和TtlCallable扩展:场景测试测试代码如下:log.info("Test")@Asyncpublicvoidtest(){log.info("测试1")}userService.findByUserName("gu");api网关打印的logELK聚合日志可以通过traceId查询整个链接日志。当系统出现异常时,可以直接通过异常日志的traceId值在日志中心查询请求的所有日志信息,类似下图:综上所述,分布式日志跟踪已经完成,使得可以很好的查看整个微服务的log链接,谢谢!!!作者:香雪课堂链接:https://www.jianshu.com/p/a3ebc9249b69来源:简书