← 中文资源

简体中文版本

采样高流量 Info 日志而不丢错误

按请求稳定采样 info 日志以降低日志量和成本,同时让警告、错误和慢请求保持全量记录,避免事故线索被采样掉。

阅读英文原文

概述

逐行随机丢弃日志会让同一次请求的日志支离破碎:可能记录了开始,却漏掉数据库调用和结束。正确做法是用稳定的请求 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 由客户端提供,需要校验格式或重新生成,否则调用方可以影响采样结果。