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

Spring Micrometer:如何在所有子Span中标记Baggage?首个观测未生效求助

问题:HTTP请求首个观测未携带Baggage中的truckId

我们正在构建一款卡车管理应用,许多请求包含truckId字段,希望该字段能标记在所有服务的相关子Span中。我们尝试在请求到达REST控制器时立即添加Baggage,并使用ObservationFilter将Baggage作为高基数值附加到每个Trace中。该方案对大部分观测有效,但接收HTTP请求时的首个观测未包含Baggage,似乎ObservationFilter未被调用。附上代码示例及Jaeger追踪图,恳请提供帮助!

客户端控制器代码

@PostMapping("/trucks/{truckId}")
public ResponseEntity<Void> PostRequestTruckSelfCheck(@PathVariable("truckId") String truckId) {

        // 获取当前Span并添加Baggage
        try (BaggageInScope baggageInScope = tracer.createBaggageInScope("truckId", truckId)) {

            HttpStatusCode code = Observation.createNotStarted("request.selfcheck.truck", observationRegistry)
                .contextualName("handle-truck-selfcheck-request")
                .observe(() -> selfcheckService.requestTruckSelfCheck(truckId));
            return new ResponseEntity<>(code);
        }
    }

观测配置代码

@Configuration
public class ObservabilityConfigs {
    // 监控相关Bean
    @Bean
    ObservationRegistry observationRegistry(Tracer tracer) {
        ObservationRegistry observationRegistry =  ObservationRegistry.create();
        observationRegistry.observationConfig().observationFilter(new BaggageObservationFilter(tracer));
        return observationRegistry;
    }

    // 注册切面以支持@Observed注解
    @Bean
    ObservedAspect observedAspect(ObservationRegistry observationRegistry) {
        return new ObservedAspect(observationRegistry);
    }


    @RequiredArgsConstructor
    public static class BaggageObservationFilter implements ObservationFilter {
        private final Tracer tracer;

        @Override
        @NonNull
        public Observation.Context map(Observation.Context context) {
            // 将Baggage添加到观测上下文
            tracer.getAllBaggage().entrySet().stream()
                .map(entry -> KeyValue.of(entry.getKey(), entry.getValue()))
                .forEach(context::addHighCardinalityKeyValue);
            return context;
        }
    }

Jaeger追踪图

Jaeger追踪图


解决方案

问题根源在于HTTP请求对应的观测(即首个Span)是在请求到达控制器方法之前就创建的——Spring MVC的观测逻辑会在DispatcherServlet处理请求阶段初始化观测,而你是在控制器方法内部才添加Baggage,此时首个观测已经完成初始化,ObservationFilter无法对它生效。

有两种可行的修复方式:

方式一:在请求进入控制器前添加Baggage

使用Spring MVC的HandlerInterceptor,在请求到达控制器方法之前就将truckId添加到Baggage中:

@Component
public class TruckIdBaggageInterceptor implements HandlerInterceptor {
    private final Tracer tracer;

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

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        @SuppressWarnings("unchecked")
        Map<String, String> uriVariables = (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
        String truckId = uriVariables.get("truckId");
        if (truckId != null) {
            tracer.createBaggageInScope("truckId", truckId);
        }
        return true;
    }
}

注册拦截器:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    private final TruckIdBaggageInterceptor truckIdBaggageInterceptor;

    public WebConfig(TruckIdBaggageInterceptor truckIdBaggageInterceptor) {
        this.truckIdBaggageInterceptor = truckIdBaggageInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(truckIdBaggageInterceptor)
                .addPathPatterns("/trucks/{truckId}");
    }
}

这样HTTP请求观测初始化时,Baggage已存在,ObservationFilter就能将truckId附加到首个Span上。

方式二:自定义ServerHttpRequestObservationConvention

直接修改HTTP请求观测的元数据,从路径变量提取truckId并添加到高基数标签,无需依赖Baggage:

@Component
public class CustomHttpRequestObservationConvention extends DefaultServerHttpRequestObservationConvention {
    @Override
    public KeyValues getHighCardinalityKeyValues(ServerHttpRequestObservationContext context) {
        KeyValues defaultValues = super.getHighCardinalityKeyValues(context);
        String truckId = context.getPathPatternMatch().getUriVariables().get("truckId");
        if (truckId != null) {
            return defaultValues.and("truckId", truckId);
        }
        return defaultValues;
    }
}

这种方式更直接,专门针对HTTP请求的首个Span添加标签。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:45:16