如何结合Micrometer的@Observed与B3 Trace ID并注入方法?
关联B3 Trace-ID与Micrometer @Observed方法调用的实现方案
核心思路
要把B3-Trace-ID和@Observed注解生成的观测Span关联,本质是把B3的追踪标识注入到Micrometer的Span上下文或标签中,主要有三种落地方式:
1. 直接给观测Span添加B3标签(快速实现)
在被@Observed注解的方法内部,通过Micrometer的TracerAPI获取当前Span,手动添加B3-Trace-ID标签,这样观测数据里就能直接看到关联的追踪ID。
import io.micrometer.tracing.Tracer; import io.micrometer.observation.annotation.Observed; import org.springframework.stereotype.Component; @Component public class BusinessService { private final Tracer tracer; public BusinessService(Tracer tracer) { this.tracer = tracer; } @Observed(name = "order.process") public void processOrder(String b3TraceId) { // 将B3 Trace-ID绑定到当前观测Span if (tracer.currentSpan() != null) { tracer.currentSpan().tag("b3.trace.id", b3TraceId); } // 业务逻辑代码... } }
2. 自动关联到追踪上下文(链路打通)
如果项目集成了Micrometer Tracing的B3适配,可在请求入口(过滤器/拦截器)解析B3头并构建TraceContext,后续@Observed方法生成的Span会自动继承这个Trace-ID,实现网络调用与方法观测链路的完全串联。
import io.micrometer.tracing.TraceContext; import io.micrometer.tracing.TraceContextBuilder; import io.micrometer.tracing.Tracer; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServletRequest; import org.springframework.stereotype.Component; @Component public class B3TraceContextFilter implements Filter { private final Tracer tracer; public B3TraceContextFilter(Tracer tracer) { this.tracer = tracer; } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { HttpServletRequest httpReq = (HttpServletRequest) request; String b3TraceId = httpReq.getHeader("B3-Trace-ID"); String b3SpanId = httpReq.getHeader("B3-Span-ID"); if (b3TraceId != null) { // 构建包含B3标识的TraceContext TraceContext traceContext = TraceContextBuilder.create() .traceId(b3TraceId) .spanId(b3SpanId != null ? b3SpanId : tracer.nextSpan().context().spanId()) .build(); // 将上下文绑定到当前线程,后续@Observed方法自动复用 try (Tracer.SpanInScope ignored = tracer.withSpan(tracer.nextSpan(traceContext))) { chain.doFilter(request, response); } } else { chain.doFilter(request, response); } } }
3. 切面统一处理(批量方法适配)
如果多个@Observed方法都需要关联B3-Trace-ID,可自定义切面拦截所有被@Observed注解的方法,统一注入标签,避免重复代码。
import io.micrometer.tracing.Tracer; import io.micrometer.observation.annotation.Observed; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; import jakarta.servlet.http.HttpServletRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @Aspect @Component public class ObservedMethodB3Aspect { private final Tracer tracer; public ObservedMethodB3Aspect(Tracer tracer) { this.tracer = tracer; } @Before("@annotation(Observed)") public void injectB3TraceId(JoinPoint joinPoint) { ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attrs != null) { HttpServletRequest request = attrs.getRequest(); String b3TraceId = request.getHeader("B3-Trace-ID"); if (b3TraceId != null && tracer.currentSpan() != null) { tracer.currentSpan().tag("b3.trace.id", b3TraceId); } } } }
方案选择建议
- 仅需在观测数据中标记B3 ID:选方法内加标签的方式;
- 需要打通网络调用与方法观测的完整链路:选入口处绑定TraceContext的方式;
- 大量方法需要统一处理:选切面方案。
内容的提问来源于stack exchange,提问作者Dmytro Nasyrov
相关产品推荐
相关产品推荐

