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

Java OpenTelemetry MongoDB扩展获取无效SpanContext问题

解决OpenTelemetry MongoDB扩展中SpanContext无效(全零值)的问题

尝试给MongoDB查询添加traceId注释,拦截com.mongodb.client.MongoCollection的find()方法时,Advice中获取的SpanContext始终无效,traceId/spanId全零,但JDBC场景下相同逻辑能正常获取有效Span。

问题原因

  1. 时机不匹配:find()方法仅用于构建查询对象(FindIterable),并未实际执行查询操作。此时OpenTelemetry官方Mongo Instrumentation还未创建对应的Span,当前线程无有效Span上下文。
  2. 线程上下文丢失:Mongo驱动可能在异步线程中执行实际查询,若上下文未正确传递,异步线程无法获取父Span。
  3. Instrumentation执行顺序错误:自定义Instrumentation的order设为1,会先于官方Mongo Instrumentation执行,此时官方尚未创建Span,自然无法获取有效上下文。

解决方案

方案1:拦截实际执行查询的方法

放弃拦截find(),改为拦截Mongo驱动中实际发送查询请求的方法(这类方法是官方Instrumentation创建Span的节点,此时能获取有效SpanContext)。以同步驱动为例:

调整MongoQueryInstrumentation代码:

public class MongoQueryInstrumentation implements TypeInstrumentation {

  @Override
  public ElementMatcher<TypeDescription> typeMatcher() {
    // 匹配同步操作执行器类
    return named("com.mongodb.internal.operation.OperationExecutor");
  }

  public void transform(TypeTransformer transformer) {
    // 拦截execute方法,参数匹配根据Mongo版本调整
    transformer.applyAdviceToMethod(
        named("execute")
            .and(takesArgument(0, named("com.mongodb.internal.operation.ReadOperation")))
            .and(takesArgument(1, named("com.mongodb.ReadPreference")))
            .and(takesArgument(2, named("com.mongodb.internal.connection.ConnectionDescription")))
            .and(isPublic()),
        AdvicesExecute.class.getName());
  }

  @SuppressWarnings("unused")
  public static class AdvicesExecute {

    @Advice.OnMethodEnter(suppress = Throwable.class)
    public static void onEnter(
        @Advice.Argument(0) ReadOperation<?> operation,
        @Advice.Local("traceId") String traceId) {
      SpanContext spanContext = Java8BytecodeBridge.currentSpan().getSpanContext();
      if (spanContext.isValid()) {
        traceId = spanContext.getTraceId();
        // 针对查询操作添加traceId注释
        if (operation instanceof FindOperation<?>) {
          ((FindOperation<?>) operation).comment(traceId);
        }
      }
    }
  }
}

方案2:调整Instrumentation执行顺序

若坚持在FindIterable层面修改,需确保自定义Instrumentation在官方Mongo Instrumentation之后执行(官方Mongo Instrumentation的order通常为10左右,设更大的值即可):

修改MongoInstrumentationModule的order()方法:

@Override
public int order() {
  return 20; // 大于官方order值,确保后执行
}

注意:此方案可靠性较低,因为find()执行时仍可能未进入Span上下文,优先推荐方案1。

方案3:异步线程上下文传递

若使用Mongo异步驱动,需手动将Span上下文传递到异步回调线程:

// 提交异步任务时用Context包装
Context currentContext = Context.current();
executor.submit(Context.wrap(() -> {
  // 异步任务逻辑,此时可获取有效Span
}));

验证

重新打包Instrumentation并运行程序,此时获取的traceId应为非零有效值,Mongo查询的comment字段会带上正确的traceId。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 20:50:29