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

