欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Cloud 下的分布式日志追踪

程序员文章站 2022-07-03 12:34:49
...

概要

本文是《基于 ELK6.6 + Filebeat 的 Spring Cloud 日志收集》的续集(文末有链接),是在该篇文章的基础上进行的。

分布式环境下,我看查看一条日志,希望同时能看到与之相关的上下文关系,比如上一步是哪一个服务过来的,都有些什么参数。在ELK的基础上其实也能实现,就找对应时间前后很短时间内的日志,基本都是相关的,但还是得筛选,当日志的量越来越大,筛选就会变得越来越难。

本文会介绍作者当前正使用的一种方式,大概思路如下:

  • 请求到达网关时,添加 traceId 到 Header 中
  • 服务收到请求时,取出 traceId ,并生成一个 requestId ,然后把它们都加到 MDC(Logback中用于追踪记录日志的,后面会介绍) 中
  • 服务在需要调用其他服务时,保证 traceId 添加到 Header 中。
  • 配置 logback,将 MDC 中添加的两个键输出到 json 日志中。
  • Kibana 中刷新对应的索引配置。

实现过程

处理网关

本人使用 Spring Cloud Gateway

在全局过滤器 GlobalFilter 中,添加 Header 信息:

    @Bean
    @Order(-1)
    public GlobalFilter a() {
        return (exchange, chain) -> {
               /* 其他处理代码 */
                val traceId = UUID.randomUUID().toString().replace("-", "");
                return chain.filter(exchange.mutate().request(exchange.getRequest().mutate().header("X-TraceId", traceId).build()).build());
        };
    }

服务处理请求

这里使用一个切面就可以实现了:

/**
 * 基础接口切面
 */
@Slf4j
@Aspect
@Configuration
public class ApiHelperAutoConfigure {

    // API接口调用切面配置  注意:这里配置要切的Controller方法
    @Pointcut("execution(com.*(..))")
    public void executeForAPI() {
    }

    /**
     * 环绕通知
     */
    @Around("executeForAPI()")
    public Object aroundExecuteForAPI(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        val requestAttributes = RequestContextHolder.getRequestAttributes();
        val request = (HttpServletRequest) (requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST));
        MDC.put("traceId", request.getHeader("X-TraceId"));
        MDC.put("requestId", UUID.randomUUID().toString().replace("-", ""));
        /* 其他处理 */
        Object body = proceedingJoinPoint.proceed(args);
        /* 其他处理 */
        return body;
    }
}

上面代码只贴出了核心部分,直接复制粘贴可能会出问题。

服务间的调用

Spring Cloud 中的服务调用默认是使用的 Feign,对其配置即可:

@Configuration
public class FeignConfig implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        requestTemplate.header("X-TraceId",  MDC.get("traceId"));
    }

}

配置Logback

相关的核心配置如下:

    <property name="app.name" value="service-myService"/>
    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>/home/logs/${app.name}.json</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>/home/logs/${app.name}/%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>90</maxHistory>
            <totalSizeCap>200GB</totalSizeCap>
        </rollingPolicy>
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <customFields>{"app":"${app.name}"}}</customFields>
            <includeMdcKeyName>traceId</includeMdcKeyName>
            <includeMdcKeyName>requestId</includeMdcKeyName>
        </encoder>
    </appender>

includeMdcKeyName 是用于包含 MDC 中的内容的
customFields 用于添加自定义字段

处理 Kibana

位置:主页 -> Management -> Kibana(Index Patterns) -> 对应的 Index Patterns -> 刷新。

也可以找到对应的字段,增加 Popularity 值,这样就能更快的找到该值。

扩展

MDC

MDC 是日志框架下用于线程日志追踪的工具,使用 ThreadLocal 实现:

 final ThreadLocal<Map<String, String>> copyOnThreadLocal = new ThreadLocal<Map<String, String>>();

MDC 使用起来类似一个 Map,核心方法为 put、 get、 clear等。

在 logback 配置文件中可以直接引用。

链接

《基于 ELK6.6 + Filebeat 的 Spring Cloud 日志收集》