编写一个操作系统代码(几行代码轻松搞定跨系统传递)

编写一个操作系统代码(几行代码轻松搞定跨系统传递)(1)

前言

" 新项目查日志太麻烦,多台机器之间查来查去,还不知道是不是同一个请求的。打印日志时使用 MDC 在日志上添加一个 traceId,那这个 traceId 如何跨系统传递呢? "

1

背景

同样是新项目开发的笔记,因为使用的是分布式架构,涉及到各个系统之间的交互

编写一个操作系统代码(几行代码轻松搞定跨系统传递)(2)

这时候就会遇到一个很常见的问题:

  1. 单个系统是集群部署,日志分布在多台服务器上;
  2. 多个系统的日志在多台机器,但是一次请求,查日志更是难上加难。

编写一个操作系统代码(几行代码轻松搞定跨系统传递)(3)

解决方案

  1. 使用 SkyWalking traceid 进行链路追踪;
  2. 使用 Elastic APM 的 trace.id 进行链路追踪;
  3. 自己生成 traceId 并 put 到 MDC 里面。

2

MDC

MDC(Mapped Diagnostic Context)是一个映射,用于存储运行上下文的特定线程的上下文数据。因此,如果使用log4j进行日志记录,则每个线程都可以拥有自己的MDC,该MDC对整个线程是全局的。属于该线程的任何代码都可以轻松访问线程的MDC中存在的值。

如何使用 MDC

  1. log4j2-spring.xml 的日志格式中添加 %X{traceId} 配置。

<Propertyname="LOG_PATTERN"> [%d{yyyy-MM-ddHH:mm:ss.SSS}]-[%t]-[%X{traceId}]-[%-5level]-[%c{36}:%L]-[%m]%n </Property> <Propertyname="LOG_PATTERN_ERROR"> [%d{yyyy-MM-ddHH:mm:ss.SSS}]-[%t]-[%X{traceId}]-[%-5level]-[%l:%M]-[%m]%n </Property> <!--省略--> <!--这个输出控制台的配置--> <Consolename="Console"target="SYSTEM_OUT"follow="true"> <!--输出日志的格式--> <PatternLayoutcharset="UTF-8"pattern="${LOG_PATTERN}"/> </Console>

  1. 新增拦截器

拦截所有请求,从 header 中获取 traceId 然后放到 MDC 中,如果没有获取到,则直接用 UUID 生成一个。

@Slf4j @Component publicclassLogInterceptorimplementsHandlerInterceptor{ privatestaticfinalStringTRACE_ID="traceId"; @Override publicvoidafterCompletion(HttpServletrequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionarg3)throwsException{ } @Override publicvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,ModelAndViewarg3)throwsException{ } @Override publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{ StringtraceId=request.getHeader(TRACE_ID); if(StringUtils.isEmpty(traceId)){ MDC.put(TRACE_ID,UUID.randomUUID().toString()); }else{ MDC.put(TRACE_ID,traceId); } returntrue; } }

  1. 配置拦截器

@Configuration publicclassWebConfigimplementsWebMvcConfigurer{ @Resource privateLogInterceptorlogInterceptor; @Override publicvoidaddInterceptors(Interceptorregistryregistry){ registry.addInterceptor(logInterceptor) .addPathPatterns("/**"); } }

跨服务之间如何传递 traceId

  • FeignClient

因为这边使用的是 FeignClient 进行服务之间的调用,只需要新增请求拦截器即可

@Configuration publicclassFeignInterceptorimplementsRequestInterceptor{ privatestaticfinalStringTRACE_ID="traceId"; @Override publicvoidapply(RequestTemplaterequestTemplate){ requestTemplate.header(TRACE_ID,MDC.get(TRACE_ID)); } }

  • Dubbo

如果是 Dubbo 可以通过扩展 Filter 的方式传递 traceId

  1. 编写 filter

@Activate(group={"provider","consumer"}) publicclassTraceIdFilterimplementsFilter{ @Override publicResultinvoke(Invoker<?>invoker,Invocationinvocation)throwsrpcException{ RpcContextrpcContext=RpcContext.getContext(); StringtraceId; if(rpcContext.isConsumerSide()){ traceId=MDC.get("traceId"); if(traceId==null){ traceId=UUID.randomUUID().toString(); } rpcContext.setAttachment("traceId",traceId); } if(rpcContext.isProviderSide()){ traceId=rpcContext.getAttachment("traceId"); MDC.put("traceId",traceId); } returninvoker.invoke(invocation); } }

  1. 指定 filter

src |-main |-java |-com |-xxx |-XxxFilter.java (实现Filter接口) |-resources |-META-INF |-dubbo |-org.apache.dubbo.rpc.Filter (纯文本文件,内容为:xxx=com.xxx.XxxFilter)

截图如下:

编写一个操作系统代码(几行代码轻松搞定跨系统传递)(4)

测试结果如下:

编写一个操作系统代码(几行代码轻松搞定跨系统传递)(5)

" dubbo filter 相关源码地址在文末,也可以关注公众号,发送 traceid 获取。 "

其他方式

当然如果小伙伴们有使用 SkyWalking 或者 Elastic APM 也可以通过以下方式进行注入:

  1. SkyWalking

<dependency> <groupId>org.apache.skywalking</groupId> <artifactId>apm-toolkit-log4j-2.x</artifactId> <version>{project.release.version}</version> </dependency

然后将 [%traceId] 配置在 log4j2.xml 文件的 pattern 中即可

  1. Elastic APM
    1. 在启动时指定 enable_log_correlation 为 true
    2. 将 %X{trace.id} 配置在 log4j2.xml 文件的 pattern 中

3

扩展

统一日志采集

虽然有了 traceId 可以进行全链路追踪查询日志,但是毕竟也是在多台服务器上,为了提高查询效率,可以考虑将日志汇总到一起。

常用的使用方法就是基于 ELK 的日志系统:

  1. 使用 filebeat 采集日志报送到 logstash
  2. logstash 进行分词过滤等处理,输出到 Elasticsearch
  3. 使用 Kinbana 或者自己开发的可视化工具从 Elasticsearch 查询日志

编写一个操作系统代码(几行代码轻松搞定跨系统传递)(6)

结束语

本文主要记录近期开发过程中的遇到的一点问题,希望对小伙伴也有所帮助。不足之处,欢迎指正。如果小伙伴有其他的建议或者观点欢迎留言讨论,共同进步。

相关资料

[1] Log4j 2 API:

https://logging.apache.org/log4j/2.x/manual/thread-context.html

[2] SkyWalking:

https://github.com/apache/skywalking/tree/master/docs/en/setup/service-agent/java-agent

[3] Elastic APM:

https://www.elastic.co/guide/en/apm/agent/java/current/log-correlation.html

[4] Dubbo filter:

http://dubbo.apache.org/zh-cn/docs/dev/impls/filter.html

[5] 本文 Dubbo filter demo:

https://github.com/liuzhihangs/trace

- <End /> -


作者:刘志航,一个宅宅的北漂程序员。

公众号:liuzhihangs,记录工作学习中的技术、开发及源码笔记;时不时分享一些生活中的见闻感悟。欢迎大佬来指导!

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页