← 中文资源

简体中文版本

尾部采样

尾部采样在收齐一条链路全部 span 后再决定去留,从而保留完整的慢链路与失败链路。

阅读英文原文

概念定义

尾部采样是一种分布式链路采样策略:它先缓存一条链路的全部 span,待链路判断为完整后,再依据整体特征——延迟、错误状态等属性——决定是否保留。由于决策看到的是完整链路,它能在仅采样一小部分快速成功请求的同时,保留所有慢请求与失败请求;这与必须在结果未知前就下注的头部采样不同。

为什么重要

失败与长尾延迟相对于快速成功请求的稳定流是稀少的。统一的百分之一采样很难包含正在排查的那起故障。尾部采样把这个比例倒过来:保留全部错误与慢链路,只保留其余的一小部分,从而在有价值处数据密集、在无价值处成本低廉。

工程实践

缓冲时长要足以覆盖最慢的链路,又不能长到内存无界增长,并把一条链路的全部 span 路由到同一个决策点——集中式 collector 或按 trace id 分片。组合策略:保留错误与高延迟、对成功按比例采样、始终保留带特定属性的链路。同时输出采样率计数器,便于后端正确加权统计。

代码示例

from dataclasses import dataclass, field
import random

@dataclass
class Span:
    name: str
    duration_ms: float
    error: bool = False

@dataclass
class Policy:
    slow_ms: float = 500.0
    keep_errors: bool = True,
    fallback_rate: float = 0.01

def decide(spans: list['Span'], policy: Policy, rng: random.Random | None = None) -> tuple[bool, str]:
    rng = rng or random.Random()
    if not spans:
        return False, 'empty'
    if policy.keep_errors and any(s.error for s in spans):
        return True, 'error'
    slowest = max(s.duration_ms for s in spans)
    if slowest >= policy.slow_ms:
        return True, 'slow'
    if rng.random() < policy.fallback_rate:
        return True, 'sampled'
    return False, 'dropped'
export class LogSampler {
  constructor({ rate = 0.01, alwaysKeep = () => false } = {}) {
    this.rate = rate;
    this.alwaysKeep = alwaysKeep;
    this.seen = new Set();
  }

  shouldEmit(signature) {
    if (this.alwaysKeep(signature)) return true;
    if (!this.seen.has(signature)) {
      this.seen.add(signature);
      return true;
    }
    return Math.random() < this.rate;
  }

  emit(signature, write) {
    if (!this.shouldEmit(signature)) return false;
    write({ signature, sampledRate: this.rate });
    return true;
  }
}