nginx 高流量下应采用 map 指令条件采样:基于时间戳或 ip 哈希实现概率采样,按响应耗时分层记录(快请求抽样、慢请求全量),5xx 错误强制全量落盘,并注意 if=仅支持0/1、log_format需前置、目录写权限三大约束。

大流量下全量记录 Nginx 日志,很快就会压垮磁盘 I/O、推高存储成本,还拖慢日志分析效率。真正可行的解法不是“少存一点”,而是用 原生、轻量、可预测 的方式做条件采样——Nginx 自带的 map 指令就是最稳的选择。
用 map 构建概率开关,不依赖随机数
Nginx 的 map 本身不支持 rand(),但能结合稳定变量构造近似均匀分布:
-
基于时间戳采样:取
$msec整数部分对 100 取模,map {$msec%100} $sample { default 0; ~^[0-4]$ 1; }→ 约 5% 采样率,简单且无状态 -
基于 IP 哈希采样:用
hash $remote_addr crc32(需启用ngx_http_map_module),再取模,保证同一客户端日志始终被采或不被采,利于问题复现 -
避免用 $request_id 直接采样:该变量默认未启用,若未配置
random模块,值为空,所有请求都会落入default分支,采样失效
按响应耗时分层采样,兼顾可观测性
关键问题是:不能只看“比例”,还要保重点。比如快请求可以抽样,慢请求必须全量。
- 用正则匹配
$request_time(map不支持数值比较):
map $request_time $log_level {
default skip;
~^[0-9]*\.[0-5][0-9]{2}$ sample;
~^[0-9]*\.[6-9][0-9]{2}$ full;
~^[1-9][0-9]*\.[0-9]{3}$ full;
} - 后续通过
access_log ... if=$log_level绑定不同输出路径,例如:access_log /var/log/nginx/sample.log main if=sample;access_log /var/log/nginx/slow.log main if=full; - 对
slow.log启用buffer=32k flush=1s,对sample.log可设更大 buffer 和更长 flush 间隔,降低刷盘压力
错误与异常请求必须保底全量
采样不是为了省空间而牺牲故障定位能力。5xx、连接中断、超时等信号一旦丢失,排查就变盲人摸象。
- 单独定义一个
map捕获异常状态:map $status $error_loggable { ~^[5] 1; default 0; } - 加一条强制日志:
access_log /var/log/nginx/error_access.log main if=$error_loggable; - 这条规则优先级高于普通采样,确保所有 5xx 请求无论快慢、无论 IP,100% 落盘
落地前注意三个硬约束
配置写得再漂亮,踩中这几个坑就直接失效:
-
if=后面的变量值只能是整数0或1,不能是字符串"0"或空值 -
log_format必须在map块之前定义,否则变量引用会报错 - 目标日志目录(如
/var/log/nginx/)需确保 Nginx worker 进程有写权限,否则 silently 丢弃日志











