You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenTelemetry无法生成唯一Trace ID,10次迭代后重复如何解决?

问题背景

在Spring项目中手动实现OpenTelemetry,自定义了OpenTelemetry Bean,并在控制器和服务类中手动创建Span,但运行10次迭代后发现Trace ID出现重复。

自定义OpenTelemetry Bean代码

@Bean
public static OpenTelemetry openTelemetry() {
    Resource resource = Resource.getDefault();
    SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
                 .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create()))
            .setResource(resource)
            .build();

    SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
            .registerMetricReader(PeriodicMetricReader.builder(LoggingMetricExporter.create()).build())
            .setResource(resource)
            .build();

    SdkLoggerProvider sdkLoggerProvider = SdkLoggerProvider.builder()
            .addLogRecordProcessor(BatchLogRecordProcessor.builder(SystemOutLogRecordExporter.create()).build())
            .setResource(resource)
            .build();

    OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
            .setTracerProvider(sdkTracerProvider)
            .setMeterProvider(sdkMeterProvider)
            .setLoggerProvider(sdkLoggerProvider)
            .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), W3CBaggagePropagator.getInstance())))
            .buildAndRegisterGlobal();

    return openTelemetry;
}

控制器和服务类中的Span创建代码

控制器注入并初始化Tracer:

private Tracer tracer;

private OpenTelemetry openTelemetry;

AccountInfoController(OpenTelemetry openTelemetry) {
    tracer = openTelemetry.getTracer(AccountInfoController.class.getName(), "0.1.0");
}

控制器创建Span:

Span span = tracer.spanBuilder(spanName).startSpan();

服务类创建Span(依赖控制器传入的parentSpan):

Context.current().with(parentSpan).makeCurrent();
Span serviceSpan = tracer.spanBuilder(spanName)
        .setParent(Context.current().with(Span.current()))
        .startSpan();

问题原因

Trace ID重复的核心原因是Span和Context的手动管理不规范:

  1. 控制器中创建的Span没有正确结束和释放,导致后续请求复用了之前的Context(包含旧的Trace ID)
  2. 服务类中手动调用Context.makeCurrent()的方式错误,没有正确隔离不同请求的上下文,造成Trace ID串用
  3. 默认的OpenTelemetry Trace ID生成器(RandomIdGenerator)生成的是128位随机ID,本身足够唯一,重复问题几乎不可能由生成器本身导致,必然是上下文管理失误。

解决方案

方案1:改用OpenTelemetry自动 instrumentation(推荐)

对于Spring Boot项目,优先使用官方提供的自动集成starter,无需手动创建Bean和管理Span,自动生成唯一Trace ID:

  1. 引入依赖(以Maven为例):
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-spring-boot-starter</artifactId>
    <version>1.32.0</version> <!-- 使用最新稳定版 -->
</dependency>
  1. 在application.properties中配置基本信息:
otel.service.name=your-service-name
otel.traces.exporter=logging
otel.metrics.exporter=logging
otel.logs.exporter=logging
  1. 移除自定义的OpenTelemetry Bean和手动创建Span的代码,框架会自动为HTTP请求生成根Span,并自动管理Trace ID和上下文传递。

方案2:修正手动Span/Context管理逻辑

如果必须手动实现,需规范Span的创建和释放流程:

控制器代码修正(使用try-with-resources自动结束Span)

// 在控制器方法内
try (Span span = tracer.spanBuilder(spanName).startSpan()) {
    // 将当前Span设置为上下文当前,供下游自动继承
    try (Scope scope = span.makeCurrent()) {
        // 执行业务逻辑,调用服务类
        accountService.processRequest();
    }
} // try块结束时自动调用span.end(),释放上下文

服务类代码修正(正确关联父Span)

无需手动修改全局Context,直接通过SpanBuilder关联父Span:

// 服务类方法
public void processRequest() {
    // 自动从当前上下文获取父Span
    try (Span serviceSpan = tracer.spanBuilder("AccountService.processRequest")
            .setParent(Context.current()) // 自动继承当前上下文的父Span
            .startSpan()) {
        // 服务层业务逻辑
    }
}

如果必须手动传递父Span,可改为:

public void processRequest(Span parentSpan) {
    try (Span serviceSpan = tracer.spanBuilder("AccountService.processRequest")
            .setParent(parentSpan) // 直接使用传入的父Span
            .startSpan()) {
        // 服务层业务逻辑
    }
}

方案3:显式指定ID生成器(可选,用于特殊场景)

如果需要自定义Trace ID生成规则(默认已足够唯一),可以在SdkTracerProvider构建时指定:

SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
        .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create()))
        .setResource(resource)
        .setIdGenerator(RandomIdGenerator.getInstance()) // 默认就是这个,可替换为自定义实现
        .build();

内容的提问来源于stack exchange,提问作者Keerthana C

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 05:12:37