awk统计Nginx日志关键在于准确定位字段(如$1 IP、$9状态码、$13 request_time)、规避引号空格导致的切分错误,并采用合理聚合逻辑;需根据log_format确认真实客户端IP位置,结合时间截取、状态码与响应时间联动分析及异常模式筛查提升准确性。

直接用 awk 统计 Nginx 日志,关键不是写长命令,而是找准字段、避开空格陷阱、用对聚合逻辑。标准 combined 格式下,$1 是 IP,$9 是状态码,$13 是 request_time——但注意:$4(时间)、$6(方法)、$7(路径)、$11(referer)等字段带引号和空格,不能简单按空格切分,否则字段会整体错位。
统计访问最多的 IP(防错版)
很多教程直接写 awk '{print $1}' | sort | uniq -c,但若日志里有 IPv6 或代理头(如 X-Forwarded-For),$1 就不是真实客户端 IP。更稳妥的做法是:
- 确认你的
log_format中真实客户端 IP 字段位置,比如用了$http_x_forwarded_for,那就要先提取它再取第一个 IP - 基础统计(假设 $1 可信):
awk '{ip[$1]++} END {for (i in ip) print ip[i], i}' access.log | sort -nr | head -10 - 加去重保护:避免同一 IP 短时高频刷屏,可结合时间字段做分钟级去重(见下条)
按时间粒度统计请求量(秒/分/小时)
Nginx 日志的 形如 [15/Oct/2023:13:25:36,固定长度,适合字符串截取:
- 每秒请求数(精确到秒):
awk '{t=substr($4,2,19); cnt[t]++} END {for (i in cnt) print i, cnt[i]}' access.log | sort - 每分钟请求数(去掉秒):
awk '{t=substr($4,2,16); cnt[t]++} END {for (i in cnt) print i, cnt[i]}' access.log | sort -nr | head -20 - 只看最近 5 分钟:
awk -v now=$(date -d '5 minutes ago' '+%d/%b/%Y:%H:%M') '$4 > "["now {print}' access.log | wc -l
状态码与响应时间联动分析
单看 500 没意义,要结合请求方法和耗时才能定位问题:
- 找出所有耗时 >2 秒且返回 500 的 POST 接口:
awk '$9==500 && substr($6,2,length($6)-2)=="POST" && $13>2 {print $7, $13}' access.log - 统计各状态码中平均响应时间:
awk '$9 ~ /^[2345][0-9][0-9]$/ {sum[$9]+=$13; cnt[$9]++} END {for (s in sum) printf "%s: %.3f s (%d req)\n", s, sum[s]/cnt[s], cnt[s]}' access.log - 注意:$13 是 request_time(单位秒,含小数),别误当毫秒用;若日志里没开启
$request_time,该字段不存在
快速筛查异常模式(组合条件)
不是所有异常都靠状态码,比如大量 200 但返回空内容、或高频扫描路径:
- 返回 200 却 body_bytes_sent ≤ 10 字节(可能是探测或空响应):
awk '$9==200 && $10 - 高频访问敏感路径(如 /wp-login.php、/admin):
awk '$7 ~ /\/(wp-login|admin|phpmyadmin)/ {url[$7]++} END {for (u in url) if (url[u]>50) print u, url[u]}' access.log - 同一 IP 在 1 秒内发起 ≥5 次请求(疑似脚本):
awk '{t=substr($4,2,19); ip_t[$1","t]++} END {for (it in ip_t) if (ip_t[it]>=5) print it}' access.log


















