如何仅当Trace包含错误Span时导出OpenTelemetry追踪数据?
如何在BatchSpanProcessor中过滤含错误Span的Trace?
BatchSpanProcessor本身没有内置的trace级过滤能力,但可以通过自定义包装SpanExporter的方式实现需求——先聚合同一Trace下的所有Span,仅当其中存在至少一个状态码为ERROR的Span时,才将该Trace的Span导出至Collector。
核心思路
每个Span都携带唯一的traceId标识所属Trace,我们可以:
- 维护一个缓存,按
traceId聚合同一Trace的所有Span - 对每个聚合后的Trace组,检查是否包含状态码为
SpanStatusCode.ERROR的Span - 仅导出符合条件的Trace的Span,并及时清理缓存避免内存泄漏
实现代码示例
1. 自定义错误Trace过滤导出器
import { BatchSpanProcessor, SpanStatusCode } from '@opentelemetry/sdk-trace-base'; import { OtlpHttpExporter } from '@opentelemetry/exporter-trace-otlp-http'; class ErrorOnlyTraceExporter { constructor(actualExporter) { this.actualExporter = actualExporter; this.traceCache = new Map(); // key: traceId, value: Span[] // 定时清理5分钟未更新的缓存,防止内存泄漏 this.cacheCleanupTimer = setInterval(() => this.cleanStaleCache(), 5 * 60 * 1000); } async export(spans, resultCallback) { // 按traceId分组Span到缓存 spans.forEach(span => { const traceId = span.spanContext().traceId; this.traceCache.set(traceId, [...(this.traceCache.get(traceId) || []), span]); }); const filteredSpans = []; const processedTraceIds = []; // 遍历缓存中的所有Trace for (const [traceId, traceSpans] of this.traceCache.entries()) { // 检查Trace是否包含错误Span const hasError = traceSpans.some(span => span.status.code === SpanStatusCode.ERROR); // 检查Trace是否已完成(所有Span都已结束) const isTraceCompleted = traceSpans.every(span => span.endTime[0] !== 0); if (hasError) { // 收集错误Trace的所有Span filteredSpans.push(...traceSpans); processedTraceIds.push(traceId); } else if (isTraceCompleted) { // 无错误且已完成的Trace,直接清理缓存 processedTraceIds.push(traceId); } } // 清理已处理的Trace缓存 processedTraceIds.forEach(id => this.traceCache.delete(id)); // 导出过滤后的Span if (filteredSpans.length > 0) { return this.actualExporter.export(filteredSpans, resultCallback); } // 无符合条件的Span,返回成功状态 resultCallback({ code: 0 }); return Promise.resolve(); } shutdown() { clearInterval(this.cacheCleanupTimer); return this.actualExporter.shutdown(); } cleanStaleCache() { const now = Date.now(); for (const [traceId, traceSpans] of this.traceCache.entries()) { // 计算Trace中最晚结束的Span时间(转毫秒) const latestEndMs = Math.max(...traceSpans.map(s => s.endTime[0] * 1000 + s.endTime[1] / 1e6)); // 清理5分钟前已结束的无错误Trace if (now - latestEndMs > 5 * 60 * 1000) { this.traceCache.delete(traceId); } } } }
2. 配置BatchSpanProcessor使用自定义导出器
// 创建标准OTLP导出器 const otlpExporter = new OtlpHttpExporter({ url: '你的Collector地址', // 其他配置如headers等 }); // 包装成仅导出错误Trace的导出器 const errorOnlyExporter = new ErrorOnlyTraceExporter(otlpExporter); // 创建BatchSpanProcessor并添加到TracerProvider const spanProcessor = new BatchSpanProcessor(errorOnlyExporter); tracerProvider.addSpanProcessor(spanProcessor);
额外优化建议
- 针对UserInteractionInstrumentation的冗余日志:可以单独配置该工具的
shouldCreateSpan选项,提前过滤掉不需要的用户交互Span(比如点击无逻辑的按钮),减少缓存压力。 - 缓存策略调整:可以根据业务场景调整缓存清理的超时时间,或者结合Trace的根Span结束事件来触发检查,更精准地判断Trace是否完成。
内容的提问来源于stack exchange,提问作者devlord
相关产品推荐
相关产品推荐

