前言

新我的项目查日志太麻烦,多台机器之间查来查去,还不晓得是不是同一个申请的。打印日志时应用 MDC 在日志上增加一个 traceId,那这个 traceId 如何跨零碎传递呢?

公众号:『 刘志航 』,记录工作学习中的技术、开发及源码笔记;时不时分享一些生存中的见闻感悟。欢送大佬来领导!

背景

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

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

  1. 单个零碎是集群部署,日志散布在多台服务器上;
  2. 多个零碎的日志在多台机器,然而一次申请,查日志更是难上加难。

解决方案

  1. 应用 SkyWalking traceid 进行链路追踪;
  2. 应用 Elastic APM 的 trace.id 进行链路追踪;
  3. 本人生成 traceId 并 put 到 MDC 外面。

MDC

MDC(Mapped Diagnostic Context)是一个映射,用于存储运行上下文的特定线程的上下文数据。因而,如果应用log4j进行日志记录,则每个线程都能够领有本人的MDC,该MDC对整个线程是全局的。属于该线程的任何代码都能够轻松拜访线程的MDC中存在的值。

如何应用 MDC

  1. log4j2-spring.xml 的日志格局中增加 %X{traceId} 配置。
<Property name="LOG_PATTERN">    [%d{yyyy-MM-dd HH:mm:ss.SSS}]-[%t]-[%X{traceId}]-[%-5level]-[%c{36}:%L]-[%m]%n</Property><Property name="LOG_PATTERN_ERROR">    [%d{yyyy-MM-dd HH:mm:ss.SSS}]-[%t]-[%X{traceId}]-[%-5level]-[%l:%M]-[%m]%n</Property><!-- 省略 --><!--这个输入控制台的配置--><Console name="Console" target="SYSTEM_OUT" follow="true">    <!--输入日志的格局-->    <PatternLayout charset="UTF-8"  pattern="${LOG_PATTERN}"/></Console>
  1. 新增拦截器

拦挡所有申请,从 header 中获取 traceId 而后放到 MDC 中,如果没有获取到,则间接用 UUID 生成一个。

@Slf4j@Componentpublic class LogInterceptor implements HandlerInterceptor {        private static final String TRACE_ID = "traceId";    @Override    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception arg3) throws Exception {    }    @Override    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView arg3) throws Exception {    }    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        String traceId = request.getHeader(TRACE_ID);        if (StringUtils.isEmpty(traceId)) {            MDC.put(TRACE_ID, UUID.randomUUID().toString());        } else {            MDC.put(TRACE_ID, traceId);        }        return true;    }    }
  1. 配置拦截器
@Configurationpublic class WebConfig implements WebMvcConfigurer {    @Resource    private LogInterceptor logInterceptor;    @Override    public void addInterceptors(InterceptorRegistry registry) {        registry.addInterceptor(logInterceptor)                .addPathPatterns("/**");    }}

跨服务之间如何传递 traceId

  • FeignClient

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

@Configurationpublic class FeignInterceptor implements RequestInterceptor {    private static final String TRACE_ID = "traceId";    @Override    public void apply(RequestTemplate requestTemplate) {        requestTemplate.header(TRACE_ID, MDC.get(TRACE_ID));    }}
  • Dubbo

如果是 Dubbo 能够通过扩大 Filter 的形式传递 traceId

  1. 编写 filter
@Activate(group = {"provider", "consumer"})public class TraceIdFilter implements Filter {    @Override    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {        RpcContext rpcContext = RpcContext.getContext();        String traceId;        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);        }        return invoker.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)

截图如下:

测试后果如下:

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 中

扩大

对立日志采集

尽管有了 traceId 能够进行全链路追踪查问日志,然而毕竟也是在多台服务器上,为了进步查问效率,能够思考将日志汇总到一起。

罕用的应用办法就是基于 ELK 的日志零碎:

  1. 应用 filebeat 采集日志报送到 logstash
  2. logstash 进行分词过滤等解决,输入到 Elasticsearch
  3. 应用 Kinbana 或者本人开发的可视化工具从 Elasticsearch 查问日志

结束语

本文次要记录近期开发过程中的遇到的一点问题,心愿对小伙伴也有所帮忙。不足之处,欢送斧正。如果小伙伴有其余的倡议或者观点欢送留言探讨,共同进步。

相干材料

  1. Log4j 2 API:https://logging.apache.org/lo...
  2. SkyWalking:https://github.com/apache/sky...
  3. Elastic APM:https://www.elastic.co/guide/...
  4. Dubbo filter:http://dubbo.apache.org/zh-cn...
  5. 本文 Dubbo filter demo:https://github.com/liuzhihang...