概念定义
日志采样是只保留一部分日志事件的做法——通常按键于消息模板或产生服务等稳定签名——从而在存储量有界的同时保住日志的统计形态。它与日志丢弃不同:采样后的流仍可分析,因为采样概率已知;而被丢弃的流则不复存在。错误与稀有事件通常被豁免或始终保留。
为什么重要
日志是最容易无限增长的信号,因为开发者会自由地加行,而每一行都要存储。这些量很大一部分是重复:同样的重试、同样的心跳。采样在保留“它确实发生过、大致多频繁”这一事实的同时压缩重复,降低存储与索引成本,又不让运维对稀有事件视而不见。
工程实践
把错误与稀有签名钉住始终保留,只对高频重复采样,并始终在每个事件上附带采样率以便重建计数。优先对结构化字段而非自由文本采样,并保留足够的本地缓冲以捕获崩溃前几秒。测量采样究竟去掉了什么,以便证明而非假设没有丢失重要类别。
代码示例
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;
}
}
#!/usr/bin/env bash
set -euo pipefail
LOG_LEVEL="${LOG_LEVEL:-info}"
declare -A LEVELS=([debug]=0 [info]=1 [warn]=2 [error]=3)
log() {
local level="${1:?level required}"
shift
local threshold="${LEVELS[${LOG_LEVEL}]:-1}"
local current="${LEVELS[${level}]:-1}"
if (( current < threshold )); then
return 0
fi
printf '{"timestamp":"%s","level":"%s","message":"%s"}\n' \
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "${level}" "$*" >&2
}
log info "job started"
log debug "this line is suppressed at info level"
log error "dependency unavailable"