目录

ELK-traceId实现跨服务日志追踪

ELK traceId实现跨服务日志追踪

在使用ELK(Elasticsearch, Logstash, Kibana)进行日志管理时,通过 traceId 实现日志跟踪是一种常见的做法。 traceId 可以帮助我们在分布式系统中追踪请求的完整链路。通过Spring的 HandlerInterceptor ,我们可以在请求进入时生成 traceId ,并将其传递到日志中。

实现思路

  1. 生成 traceId :在请求进入时生成一个唯一的 traceId ,并将其存储在 MDC (Mapped Diagnostic Context)中。
  2. 传递 traceId :在请求处理过程中,确保 traceId 能够传递到各个组件和日志中。
  3. 清理 traceId :在请求处理完成后,清理 MDC 中的 traceId ,避免内存泄漏。

代码实现

1. 创建 TraceIdInterceptor

首先,我们需要创建一个 HandlerInterceptor 实现类,用于在请求进入时生成 traceId ,并在请求完成后清理 traceId

java

复制

import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

@Component
public class TraceIdInterceptor implements HandlerInterceptor {

    private static final String TRACE_ID = "traceId";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 生成唯一的traceId
        String traceId = UUID.randomUUID().toString();
        // 将traceId放入MDC
        MDC.put(TRACE_ID, traceId);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 请求完成后清理MDC中的traceId
        MDC.remove(TRACE_ID);
    }
}
2. 注册 TraceIdInterceptor

接下来,我们需要将 TraceIdInterceptor 注册到Spring的拦截器链中。

java

复制

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private TraceIdInterceptor traceIdInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(traceIdInterceptor);
    }
}
3. 配置日志输出 traceId

在日志配置文件中,配置日志输出格式,确保 traceId 能够被打印出来。以 logback.xml 为例:

xml

复制

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - [%X{traceId}] %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

运行 HTML

在这个配置中, %X{traceId} 会从 MDC 中获取 traceId 并输出到日志中。

4. 在代码中使用 traceId

在业务代码中,你可以通过 MDC 获取 traceId ,并将其传递到其他服务或日志中。

java

复制

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    private static final Logger logger = LoggerFactory.getLogger(MyController.class);

    @GetMapping("/test")
    public String test() {
        // 获取traceId
        String traceId = MDC.get("traceId");
        logger.info("Processing request with traceId: {}", traceId);
        return "Hello, World!";
    }
}

总结

通过上述步骤,我们实现了一个简单的 traceId 日志跟踪系统。 HandlerInterceptor 在请求进入时生成 traceId 并将其放入 MDC ,在请求完成后清理 MDC 。日志配置确保 traceId 能够被输出到日志中,从而方便我们在ELK中追踪请求的完整链路。

扩展

  • 分布式系统中的 traceId 传递 :在微服务架构中, traceId 需要在服务之间传递。可以通过HTTP头、消息队列等方式传递 traceId
  • 异步任务中的 traceId :在异步任务中, traceId 可能会丢失。可以通过自定义线程池或使用 TaskDecorator 来确保 traceId 在异步任务中传递。

通过这些扩展,你可以在更复杂的系统中实现全链路的日志跟踪。