OTEL NodeJS SDK客户端采样:如何留存全量异常追踪数据
解决方案
针对你的需求,这里提供两种客户端层面的实现方案,无需启用全量尾部采样,就能确保所有含异常的追踪数据被留存到APM工具:
方案一:自定义感知异常的采样器(推荐)
这个方案会在Span创建阶段结合比例采样和异常动态修正,确保整个异常追踪链路被完整采样:
const { ParentBasedSampler, TraceIdRatioBasedSampler, SamplingDecision, SpanStatusCode, TraceFlags } = require('@opentelemetry/sdk-trace-base'); class ErrorPrioritySampler { constructor(sampleRatio) { this.ratioSampler = new TraceIdRatioBasedSampler(sampleRatio); } shouldSample(context) { // 继承父Span的采样决策 const parentSpanContext = context.spanContext(); if (parentSpanContext) { return { decision: (parentSpanContext.traceFlags & TraceFlags.SAMPLED) ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD }; } // 根Span先按比例采样,可提前标记高风险请求(比如POST接口)强制采样 const request = context.getValue('http.request'); // 基于HTTP instrumentation的示例 if (request?.method === 'POST') { return { decision: SamplingDecision.RECORD_AND_SAMPLED }; } return this.ratioSampler.shouldSample(context); } toString() { return `ErrorPrioritySampler{ratio=${this.ratioSampler.toString()}}`; } } // 替换原有采样配置 sampler: new ParentBasedSampler({ root: new ErrorPrioritySampler(options.sampleRatio), }), // 添加Span处理器,异常发生后强制修正采样决策 const errorCorrectionProcessor = new SimpleSpanProcessor({ onStart() {}, onEnd(span) { if (span.status.code === SpanStatusCode.ERROR && !(span.spanContext().traceFlags & TraceFlags.SAMPLED)) { // 强制标记该Span及链路为采样状态 span.updateTraceFlags(TraceFlags.SAMPLED); // 立即导出 yourExporter.export([span], () => {}); } } }); // 注册处理器到TracerProvider tracerProvider.addSpanProcessor(errorCorrectionProcessor);
方案二:直接强制导出异常Span
如果不想修改原有采样逻辑,可通过Span处理器单独捕获异常Span并导出,实现更轻量:
const { SimpleSpanProcessor, SpanStatusCode, TraceFlags } = require('@opentelemetry/sdk-trace-base'); const errorExportProcessor = new SimpleSpanProcessor({ onStart() {}, onEnd(span) { if (span.status.code === SpanStatusCode.ERROR) { // 确保Span被标记为采样 if (!(span.spanContext().traceFlags & TraceFlags.SAMPLED)) { span.updateTraceFlags(TraceFlags.SAMPLED); } // 直接导出到APM工具 yourExporter.export([span], (result) => { if (result.code !== 'SUCCESS') { console.error('异常Span导出失败:', result.error); } }); } } }); tracerProvider.addSpanProcessor(errorExportProcessor);
关键注意事项
- 方案一能保证完整追踪链路被采样,适合需要排查端到端问题的场景;方案二更轻量,但可能只捕获单个异常Span
- 必须确保代码中正确标记异常:使用
span.setStatus({ code: SpanStatusCode.ERROR, message: error.stack || error.message }) - 两种方案都不会显著增加吞吐量,因为异常请求占比通常远低于常规请求
内容的提问来源于stack exchange,提问作者Ján Bušfy
相关产品推荐
相关产品推荐

