← 中文资源

简体中文版本

头部采样

头部采样在链路开始、结果未知时就决定是否记录,并把该决定沿调用图向下传播。

阅读英文原文

概念定义

头部采样在请求入口做一次保留或丢弃的决定,并通过 trace 上下文向下传播,使每个下游服务一致地记录或跳过。由于决定先于执行,它无法优先保留错误或慢请求,只能随机、按速率,或依据请求进入时已知的属性来采样。其优势是简单且几乎不需要缓冲。

为什么重要

在请求量很高时,记录每条链路既不划算也无用;有代表性的样本足以回答聚合性问题。头部采样以极小的运行时开销产生该样本,且不显著改变 collector 拓扑,因此它仍是默认选择。它也易于容量规划:链路量是请求量的已知比例。

工程实践

选择一个足以调试常见问题的采样率,并按端点区分——把健康检查与下单同等采样会浪费预算。始终保留携带显式标志的链路,以便按需捕获关键流程。在错误与长尾延迟最重要的地方,应在头部决定之后叠加尾部采样,而不是靠提高速率来弥补。

代码示例

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;
  }
}