概述
逐行随机丢弃日志会让同一次请求的日志支离破碎:可能记录了开始,却漏掉数据库调用和结束。正确做法是用稳定的请求 ID 做哈希分桶,同一次请求要么整体保留,要么整体丢弃。采样率是成本控制手段,不是精确计数器;它只应作用于高频的常规成功日志。警告、错误、慢请求、认证失败和审计事件必须绕过采样,否则事故响应所依赖的信号会被削掉。
步骤
安装 pino 后,把采样逻辑集中到一个模块:
const RATE = Number(process.env.INFO_SAMPLE_RATE ?? 0.1);
export function sampled(requestId: string): boolean {
if (!Number.isFinite(RATE) || RATE >= 1) return true;
if (RATE <= 0) return false;
const bucket =
createHash('sha256').update(requestId).digest().readUInt32BE(0) / 0xffffffff;
return bucket < RATE;
}
在请求入口生成或校验 x-request-id,写入响应头,并在 res.on('finish') 时统一记录完成日志。状态码 5xx 使用 logger.error;4xx 或耗时超过 1000ms 使用 logger.warn;只有快速成功的 2xx 才调用采样版 info。这样错误和慢请求始终可见,成功请求的日志量约为原来的十分之一。启动时只解析一次环境变量,并校验取值在 0 到 1 之间。
验证
先做分布检查:用 10000 个 req-${i} 计算哈希桶,INFO_SAMPLE_RATE=0.1 时命中数应落在 900 到 1100 之间,比例约 0.09 到 0.11。部署后用 kubectl logs deploy/api --since=30m | jq -r 'select(.level == 50) | .msg' 确认错误仍在;再用 jq -r 'select(.sampled == true) | .requestId' | sort -u | wc -l 统计采样请求数,并与指标中的总请求量比较。
注意事项
审计、权限变更、支付状态流转和敏感数据访问不能走采样 logger,建议使用独立 logger 和独立保留策略。采样日志不能用于精确计数,应与指标配合。排查罕见问题时临时把 INFO_SAMPLE_RATE 设为 1,并给这个覆盖设置自动过期。若请求 ID 由客户端提供,需要校验格式或重新生成,否则调用方可以影响采样结果。