Java OpenTelemetry MongoDB扩展获取无效SpanContext问题
解决OpenTelemetry MongoDB扩展中SpanContext无效(全零值)的问题
尝试给MongoDB查询添加traceId注释,拦截com.mongodb.client.MongoCollection的find()方法时,Advice中获取的SpanContext始终无效,traceId/spanId全零,但JDBC场景下相同逻辑能正常获取有效Span。
问题原因
- 时机不匹配:
find()方法仅用于构建查询对象(FindIterable),并未实际执行查询操作。此时OpenTelemetry官方Mongo Instrumentation还未创建对应的Span,当前线程无有效Span上下文。 - 线程上下文丢失:Mongo驱动可能在异步线程中执行实际查询,若上下文未正确传递,异步线程无法获取父Span。
- 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
相关产品推荐
相关产品推荐

