Spring Boot 3 OpenTelemetry Trace ID传播及创建机制与源码查询
核心传播机制
Spring Boot 3+的可观测性依赖Micrometer Tracing,底层对接OpenTelemetry或Brave(OpenTracing实现)。Trace ID跨应用传播的本质是通过请求头传递链路上下文——上游应用在请求头中携带Trace ID,下游应用解析头信息并绑定到当前线程的Tracer上下文。
默认Micrometer遵循W3C规范用traceparent头传递,但可以配置适配自定义的x-trace-id。
从x-trace-id提取并绑定上下文的源码位置
1. 请求头解析入口
对于Spring MVC场景,请求链路的入口是org.springframework.web.filter.ServerHttpObservationFilter——这个过滤器会触发Micrometer Tracing的请求解析逻辑。
具体解析请求头的逻辑在io.micrometer.tracing.handler.HttpServerRequestParser的实现类中,默认实现是DefaultHttpServerRequestParser。如果要支持自定义的x-trace-id,要么扩展这个Parser,要么通过配置指定自定义头(下文有配置示例)。
2. 上下文绑定到TracerContext
当解析到x-trace-id后,会构建SpanContext对象,然后通过Tracer的withSpan(Span span)方法将上下文绑定到当前线程的本地变量。
如果用Brave作为底层实现,核心代码在io.micrometer.tracing.brave.bridge.BraveTracer中,withSpan()会调用Brave的CurrentTraceContext完成线程绑定;如果用OpenTelemetry,则对应io.micrometer.tracing.otel.bridge.OtelTracer的实现。
Trace ID的创建逻辑
1. 无传入Trace ID时的自动生成
当请求头中没有携带x-trace-id或标准traceparent时,Micrometer会自动生成新的Trace ID:
- 生成逻辑由底层Tracer实现提供:Brave用
RandomTraceIdGenerator,OpenTelemetry用TraceIdGenerator。 - 核心触发点在
Tracer.nextSpan()方法,比如Brave实现的BraveTracer.nextSpan()会调用Brave原生Tracer生成包含新Trace ID的Span。 - 默认生成的Trace ID是16字节(32位十六进制字符串),符合W3C规范。
2. 自定义Trace ID生成规则
如果需要自定义Trace ID格式(比如业务前缀、固定长度),可以实现io.micrometer.tracing.TraceIdGenerator接口,然后注册为Spring Bean,Micrometer会自动替换默认生成器。
适配x-trace-id的配置示例
不想改源码的话,直接通过配置或自定义Bean就能支持x-trace-id:
方式1:配置文件指定自定义头
在application.yml中添加:
management: tracing: propagation: type: custom custom: headers: x-trace-id, x-span-id
方式2:自定义请求解析Bean
如果需要更灵活的逻辑,比如同时兼容traceparent和x-trace-id,可以自定义HttpServerRequestParser:
@Bean public HttpServerRequestParser customTraceIdParser(Tracer tracer) { return (request, context) -> { // 优先读取x-trace-id String traceId = request.getHeader("x-trace-id"); String spanId = request.getHeader("x-span-id"); if (traceId != null && spanId != null) { SpanContext spanContext = tracer.traceContextBuilder() .traceId(traceId) .spanId(spanId) .build(); Span span = tracer.nextSpan(spanContext); context.setSpan(span); } // 没有的话走默认逻辑 else { new DefaultHttpServerRequestParser().parse(request, context); } }; }
内容的提问来源于stack exchange,提问作者cjava

