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

Spring Boot 3.1.4 Micrometer分布式追踪异常问题求助

问题描述

我们使用Spring Boot 3.1.4,基于Micrometer实现分布式追踪,已正确添加build.gradle依赖,但仅健康检查能生成追踪数据,其余场景均无效。测试发现两种异常流程:

  • 流程1:REST请求调用服务1 → JMS发布消息至服务2方法
  • 流程2:REST请求直接调用服务2方法

上述两种场景中,接收请求的服务收到PropagatedSpan且traceFlags=00,此时无任何追踪/Span被捕获或发送。日志如下:

{opentelemetry-trace-span-key=PropagatedSpan{ImmutableSpanContext{traceId=a2ea0716a45d02a490d7cef097acdb9a, spanId=adb8f982d4294b13, traceFlags=00, traceState=ArrayBasedTraceState{entries=[]}, remote=false, valid=true}}, opentelemetry-baggage-key={}, otelTraceContext=ImmutableSpanContext{traceId=a2ea0716a45d02a490d7cef097acdb9a, spanId=adb8f982d4294b13, traceFlags=00, traceState=ArrayBasedTraceState{entries=[]}, remote=false, valid=true}}

例外情况:流程1中,服务2接收服务1请求时收到SDKSpan且traceFlags=01,此时服务2的追踪/Span可正常捕获,但服务1无追踪数据。日志如下:

{opentelemetry-baggage-key={}, opentelemetry-trace-span-key=SdkSpan{traceId=c92ee058c19d5288d6cc72d2bb9c2a3b, spanId=5797df5b5620f02d, parentSpanContext=ImmutableSpanContext{traceId=00000000000000000000000000000000, spanId=0000000000000000, traceFlags=00, traceState=ArrayBasedTraceState{entries=[]}, remote=false, valid=false}, name=, kind=SERVER, attributes=null, status=ImmutableStatusData{statusCode=UNSET, description=}, totalRecordedEvents=0, totalRecordedLinks=0, startEpochNanos=1727214899985765169, endEpochNanos=0}, otelTraceContext=ImmutableSpanContext{traceId=c92ee058c19d5288d6cc72d2bb9c2a3b, spanId=5797df5b5620f02d, traceFlags=01, traceState=ArrayBasedTraceState{entries=[]}, remote=false, valid=true}}

问题:能否让传入的追踪/Span使用SDKSpan而非PropagatedSpan(后者会阻止自定义Span生成)?或有其他解决方法?


解决方案

1. 强制开启全量采样,修正Trace Flags

traceFlags=00表示采样标志未开启,导致后续链路无法生成Span。需确保所有业务请求的采样标志设置为01:

  • 在application.yml中配置全量采样:
management:
  tracing:
    sampling:
      probability: 1.0 # 强制对所有请求开启采样
  • 若使用OpenTelemetry底层,可添加系统属性-Dotel.trace.sampler=always_on覆盖默认采样策略。

2. 替换PropagatedSpan为可操作的SdkSpan

PropagatedSpan仅携带上下文信息,无法生成子Span。可通过自定义上下文处理器将其替换为SdkSpan:

自定义Span上下文包装器

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import org.springframework.stereotype.Component;

@Component
public class PropagatedSpanWrapper {
    private final Tracer tracer;

    public PropagatedSpanWrapper(Tracer tracer) {
        this.tracer = tracer;
    }

    public Context wrap(Context context) {
        Span currentSpan = Span.fromContext(context);
        // 判断当前Span是否为PropagatedSpan
        if ("PropagatedSpan".equals(currentSpan.getClass().getSimpleName())) {
            // 创建新的SdkSpan作为当前上下文的子Span
            Span newSpan = tracer.spanBuilder("wrap-propagated-span")
                    .setParent(context)
                    .setSpanKind(Span.Kind.SERVER)
                    .startSpan();
            return context.with(newSpan);
        }
        return context;
    }
}

在请求拦截器中应用包装逻辑

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;

public class TraceContextInterceptor implements HandlerInterceptor {
    private final PropagatedSpanWrapper spanWrapper;

    public TraceContextInterceptor(PropagatedSpanWrapper spanWrapper) {
        this.spanWrapper = spanWrapper;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        Context currentContext = Context.current();
        Context wrappedContext = spanWrapper.wrap(currentContext);
        wrappedContext.makeCurrent();
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        Span.fromContext(Context.current()).end();
    }
}

注册拦截器到Spring MVC

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 WebMvcTraceConfig implements WebMvcConfigurer {
    private final PropagatedSpanWrapper spanWrapper;

    public WebMvcTraceConfig(PropagatedSpanWrapper spanWrapper) {
        this.spanWrapper = spanWrapper;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TraceContextInterceptor(spanWrapper))
                .addPathPatterns("/**")
                .excludePathPatterns("/actuator/**"); // 健康检查已有正常追踪逻辑
    }
}

3. 完善Micrometer与OpenTelemetry集成配置

Spring Boot 3.1.x中Micrometer追踪默认基于OpenTelemetry,需确保依赖和配置完整:

确认依赖配置(build.gradle)

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'io.micrometer:micrometer-tracing-bridge-otel'
    implementation 'io.micrometer:micrometer-tracing-jms' // 为JMS链路生成Span
    implementation 'io.opentelemetry:opentelemetry-exporter-otlp' // 发送追踪数据到OTEL Collector
}

配置链路传播协议

若使用B3等非默认协议,需在application.yml中指定:

management:
  tracing:
    propagation:
      type: tracecontext,b3 # 根据实际链路协议调整

4. 修复服务1的追踪生成问题

服务1无追踪数据通常是因为未正确生成Span:

  • REST请求:Spring MVC会自动为入口请求生成SERVER Span,检查是否有自定义拦截器覆盖了默认追踪逻辑。
  • JMS消息发送:添加micrometer-tracing-jms依赖后,框架会自动为JMS生产者生成Span,确保消息发送时上下文正确传递。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 00:45:56