基于OpenTelemetry Collector实现类New Relic分段拆解及父Span名称传递
解决方案:将根Span名称作为属性传递给所有子Span
方法一:应用端(Java自动插桩)实现(推荐)
在应用层面处理更高效,避免Collector端的性能开销。可以通过自定义SpanProcessor实现根Span名称的传递:
- 实现自定义SpanProcessor,在根Span启动时将其名称存入Baggage,子Span继承并转为属性:
public class RootSpanNameBaggageProcessor implements SpanProcessor { @Override public void onStart(Context parentContext, ReadWriteSpan span) { // 判断是否为根Span(parent_span_id为空或全0) if (span.getParentSpanId().isEmpty() || span.getParentSpanId().equals("0000000000000000")) { // 将根Span名称存入Baggage Context updatedContext = parentContext.with(Baggage.currentBuilder() .put("trace.root.name", span.getName()) .build()); span.setParentContext(updatedContext); } else { // 子Span继承父Span的Baggage,并转为自身属性 Baggage baggage = Baggage.fromContext(parentContext); baggage.getEntry("trace.root.name").ifPresent(entry -> { span.setAttribute("trace.root.name", entry.getValue()); }); } } @Override public boolean isStartRequired() { return true; } @Override public void onEnd(ReadableSpan span) {} @Override public boolean isEndRequired() { return false; } }
- 将该处理器注册到OpenTelemetry SDK:
OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(SdkTracerProvider.builder() .addSpanProcessor(new RootSpanNameBaggageProcessor()) .build()) .buildAndRegisterGlobal();
如果用自动插桩Agent,可通过otel.java.span-processor.implementation配置加载自定义处理器,或打包成jar放入Agent扩展目录。
方法二:Collector端通过groupbytrace + transform处理器实现
若必须在Collector端处理,正确配置groupbytrace后,transform可访问整个追踪的Span上下文:
- 添加处理器配置:
processors: groupbytrace: wait_duration: 1s # 根据实际追踪延迟调整,确保所有Span被收集 num_traces: 10000 # 调整并发追踪数,避免内存溢出 transform: traces: queries: # 定位根Span - context: trace statements: - set(trace["root_span"], filter(spans, span -> span.parent_span_id == "0000000000000000")[0]) # 给所有子Span添加trace.root.name属性 - context: span statements: - set(attributes["trace.root.name"], trace["root_span"].name) where parent_span_id != "0000000000000000"
- 在追踪管道中启用处理器:
service: pipelines: traces: receivers: [otlp] processors: [groupbytrace, transform, spanmetrics] exporters: [otlp/tempo]
注意:groupbytrace会增加Collector内存占用,需根据流量调整参数避免OOM。
方法三:Grafana查询层关联(无需修改链路)
若不想修改数据处理链,可在Grafana中直接关联Tempo与Prometheus数据:
在面板中使用Prometheus查询Span指标,同时通过Tempo获取对应追踪的根Span名称作为聚合维度,示例表达式:
sum(otel_span_duration_sum) by (trace_id) | with trace_data = tempo_trace_data(trace_id) | with root_span = filter(trace_data.spans, s -> s.parent_span_id == "0000000000000000")[0] | sum by (root_span.name) (value)
这种方法无需修改链路,但依赖Grafana的Tempo-Prometheus关联能力,查询性能可能受影响。
内容的提问来源于stack exchange,提问作者Amit Bhushan
相关产品推荐
相关产品推荐

