应将 $status 设为日志首字段并用竖线分隔,单独存储于 status.log,再用 awk 快速统计分布;stub_status 等模块仅适合实时监控,归档分析必须依赖结构化日志。

直接用 $status 变量统计 HTTP 状态码分布,核心不是“能不能取到”,而是“怎么让取值稳定、高效、可扩展”。默认日志格式里 $status 位置不固定,脚本解析容易出错;靠内存模块(如 ngx_http_status_code_counter)又无法保留历史趋势。真正实用的方案是:结构化日志 + 独立字段 + 分离存储 + 脚本驱动分析。
让 $status 成为日志首字段,降低解析成本
默认 combined 格式中 $status 在第9列,但受 $http_referer 或 $http_user_agent 中空格/引号影响,awk '{print $9}' 并不可靠。应显式前置并用强分隔符隔离:
- 定义新格式:
log_format status_first '$status | $remote_addr [$time_local] "$request" $body_bytes_sent "$http_referer" "$http_user_agent" $request_time $upstream_response_time'; - 竖线
|比空格更抗干扰,$status恒为第1字段,awk '{print $1}'即可安全提取 - 避免用
\t(制表符),某些终端或编辑器会自动转义,反而增加解析歧义
按用途分离日志文件,避免混杂干扰
不要把状态码分析日志和常规访问日志写进同一个文件。混写会导致:
- grep 或 awk 扫描范围扩大,I/O 开销翻倍
- logrotate 配置难以差异化(比如错误日志需保留30天,常规日志只留7天)
- 权限或归档策略冲突(如
upstream_status日志可能含敏感后端地址)
推荐做法:
- 为状态分析单独建
/var/log/nginx/status.log,绑定status_first格式 - 同时保留原始日志:
access_log /var/log/nginx/access.log combined; - 修改后务必执行
nginx -t && nginx -s reload生效
用一行 awk 快速产出状态码分布报表
日志结构化后,统计不再是“写脚本”,而是“组合命令”。常用场景示例:
- 全量统计各状态码次数:
awk '{c[$1]++} END {for (i in c) print i, c[i]}' /var/log/nginx/status.log | sort -n - 聚焦 5xx 错误并附带 URL:
awk '$1 ~ /^5[0-9]{2}$/ {print $1, $4, $5}' /var/log/nginx/status.log(假设第4、5字段是请求方法和路径) - 每小时 404 数量趋势:
awk '$1 == "404" {gsub(/\[/, "", $2); print substr($2, 1, 13)}' /var/log/nginx/status.log | sort | uniq -c
补充:实时监控与内存统计的适用边界
像 stub_status 或 ngx_http_status_code_counter 这类模块,适合看“当前运行态”:
-
stub_status显示活跃连接、总请求数等聚合指标,不区分状态码 -
ngx_http_status_code_counter可暴露各状态码累计值,但 nginx reload 后清零,无法回溯 - 它们轻量、低延迟,适合嵌入 Grafana 做实时大盘,但不能替代日志分析做根因定位或周期复盘
真正需要归档、审计、同比环比的场景,必须依赖结构化日志 + 定时分析任务(如每天凌晨用 cron 跑一次 awk + gzip 归档)。











