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

如何仅当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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.11 19:12:36