Web 服务器日志分析是每一个运维和开发者都绕不开的基本功。每天你的 Nginx、Apache 都在默默记录着每一次请求:谁来了、看了什么、返回了什么状态码、花了多久。本文用 GoAccess 与一行行的命令行,带你把原始日志变成可执行的洞察——看清流量、揪出异常、拦住攻击。
一、为什么日志分析是运维基本功
日志是系统最诚实的”监控摄像头”。访问量暴涨时,它能告诉你哪个接口被刷;安全事件发生时,它能还原攻击者踩点的路径;性能抖动时,它能定位最慢的那 1% 请求。与其等用户投诉,不如每天花十分钟看一眼日志。本文聚焦 Web 访问日志(access log)的分析,不展开采集管道——如果你需要统一采集多台机器,可以回顾 Fluentd 日志采集实战。
二、先认识 Nginx 的默认日志格式
Nginx 默认使用 combined 格式,每一行包含 9 个字段。理解字段含义是后续一切分析的前提。如果你用的是反向代理,建议参考 Nginx 反向代理完整配置 把真实客户端 IP($http_x_forwarded_for)也记下来。
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" '
'$status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
其中 $request_time 是请求总耗时,$upstream_response_time 是后端处理耗时,两者差值能暴露网络或代理开销。下面所有命令行示例都假设日志位于 /var/log/nginx/access.log。
三、命令行三板斧:awk / grep / sort
在装任何工具之前,先掌握三条命令。它们零依赖、随取随用,适合写进排障脚本。
3.1 找出访问最频繁的 IP
awk '{print $1}' /var/log/nginx/access.log \
| sort | uniq -c | sort -nr | head -20
3.2 统计状态码分布
awk '{print $9}' /var/log/nginx/access.log \
| sort | uniq -c | sort -nr
3.3 列出最慢的 10 个请求
awk '{print $NF, $7, $1}' /var/log/nginx/access.log \
| sort -nr | head -10
下表是日常最常用的一组成熟命令,建议存进 dotfiles 或脚本库随时调用。
| 目标 | 核心命令 | 说明 |
|---|---|---|
| Top IP | awk '{print $1}' | sort | uniq -c | sort -nr | 识别爬虫与刷量 |
| 404 清单 | awk '$9==404 {print $7}' | sort | uniq -c | sort -nr | 排查死链 |
| 5xx 趋势 | awk '$9>=500 {print $7}' | 后端报错定位 |
| 爬虫 UA | awk -F'"' '{print $6}' | sort | uniq -c | 分析搜索引擎 |
四、用 GoAccess 做实时可视化
命令行适合一次性排查,但要看趋势、做汇报,还是需要可视化。GoAccess 是一个单文件、零外部依赖的实时日志分析器,能直接在终端显示面板,也能导出成自包含的 HTML 仪表盘。
4.1 安装与终端实时面板
# Debian / Ubuntu
sudo apt-get install -y goaccess
# 实时跟踪日志(终端交互面板)
goaccess /var/log/nginx/access.log \
--log-format=COMBINED \
--real-time-html
4.2 生成可分享的 HTML 报告
goaccess /var/log/nginx/access.log \
--log-format=COMBINED \
--output=report.html \
--ignore-panel=GEO_LOCATION
生成的 report.html 自带访客地图、热门页面、操作系统、浏览器、状态码、小时维度趋势等面板,发给团队或贴到内网即可。注意:报告含原始访问数据,不要公开到公网。
五、从日志里抓异常与攻击
攻击者通常会先用自动化工具扫描漏洞。这些行为在日志里特征极其明显:大量 404、奇奇怪怪的 URL、可疑的 User-Agent。下面几条命令能快速筛出高危信号。
# 1. 路径遍历 / SQL 注入 探测
grep -Ei '(\.\./|union select|or 1=1|/etc/passwd|\$&|<script)' \
/var/log/nginx/access.log | awk '{print $1, $7}'
# 2. 单 IP 短时间高频 404(疑似扫描器)
awk '$9==404 {print $1}' /var/log/nginx/access.log \
| sort | uniq -c | sort -nr | awk '$1>200'
# 3. 异常 User-Agent(空 UA、sqlmap、nmap、nikto 等)
awk -F'"' '$6 ~ /(sqlmap|nikto|masscan|^-?$|python-requests)/ {print $1}' \
/var/log/nginx/access.log
| 攻击特征 | 日志中的表现 | 应对 |
|---|---|---|
| 目录遍历 | URL 含 ../、/etc/passwd | Nginx 拒绝、WAF 规则 |
| SQL 注入 | 含 union select、or 1=1 | 参数化查询 + 限流 |
| 扫描器 | UA 为 sqlmap/nikto,单 IP 海量 404 | Fail2ban 封禁 |
| CC 攻击 | 单 IP QPS 异常高 | Nginx 限流 |
六、慢请求与性能洞察
性能问题往往藏在长尾里。配合前面开启的 $request_time,可以找出拖慢整体体验的接口,再结合 PostgreSQL 慢查询优化 一起定位是网络、代理还是数据库慢。
# 找出 request_time 大于 1 秒、且占比较高的请求
awk '$NF > 1.0 {print $NF, $7, $1}' /var/log/nginx/access.log \
| sort -nr | head -30
# 统计各接口平均耗时(需要 gawk 支持数组)
awk '{url[$7]+=$NF; cnt[$7]++} END \
{for (u in url) printf "%.3f %s\n", url[u]/cnt[u], u}' \
/var/log/nginx/access.log | sort -nr | head -15
七、把日志分析写进自动化
手动看日志不可持续。把分析脚本放进定时任务或 CI,每天自动产出报告,才是”可运营”的做法。下面用一段 cron 脚本每天生成并归档报告,容器化部署时日志路径参考 Docker 编排实战 的 volume 挂载。
#!/usr/bin/env bash
# /usr/local/bin/daily_log_report.sh
LOG=/var/log/nginx/access.log
OUT=/var/www/reports/$(date +%F).html
goaccess "$LOG" --log-format=COMBINED --output="$OUT"
# 找出当日高危 IP,写入告警文件
awk '$9==404 {print $1}' "$LOG" \
| sort | uniq -c | sort -nr | awk '$1>500' \
> /tmp/suspect_ips.txt
[ -s /tmp/suspect_ips.txt ] && \
mail -s "Nginx 日志告警 $(date +%F)" admin@example.com < /tmp/suspect_ips.txt
在 crontab 中加入 0 8 * * * /usr/local/bin/daily_log_report.sh 即可每天早 8 点自动跑。需要把报告推送到对象存储或内网看板时,也可封装成 GitHub Actions 工作流 触发。
八、落地清单
| 阶段 | 动作 | 产出 |
|---|---|---|
| 接入 | 确认 log_format 含 $request_time、X-Forwarded-For | 可分析日志 |
| 日常 | awk/grep 三板斧排查异常 | 即时洞察 |
| 可视 | GoAccess 生成 HTML 报告 | 趋势看板 |
| 防护 | Fail2ban + Nginx 限流拦截扫描 | 降低风险 |
| 自动 | cron / CI 每日报告 | 可持续运营 |
八、日志轮转与归档(logrotate)
日志不是只增不减。如果不做轮转,单文件会无限膨胀直至打满磁盘,而磁盘满又会让 Nginx、数据库连锁故障。Linux 自带 logrotate,按天或按大小切割、压缩、保留 N 份。下面是一段可直接落地的 Nginx 配置。
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
daily 表示每天轮转,rotate 14 保留 14 天,compress 启用 gzip 压缩以省空间。关键是用 kill -USR1 通知 Nginx 重新打开日志文件,否则它会一直写已被改名、实则已删除的旧文件。磁盘与 inode 相关排障可参考磁盘空间耗尽实战。
九、容器与云原生环境的日志
到了 Docker 与 Kubernetes,日志从”文件”变成了”标准输出”。容器引擎默认用 json-file 驱动把 stdout/stderr 写成 JSON 文件,位置在 /var/lib/docker/containers/。查看某个容器日志:
# 查看容器最近 100 行日志
docker logs --tail 100 <container_id>
# 跟随实时日志
docker logs -f <container_id>
# 限制日志体积,避免打满磁盘(daemon.json)
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" }
}
9.1 Kubernetes 下的日志聚合
在 K8s 里 Pod 会漂移、会重建,本地日志不可靠,必须集中收集。常用方案是 DaemonSet 形式的采集器(如 Fluent Bit)把每个节点上的容器日志转发到 Elasticsearch 或 Loki。排障时先用 kubectl logs 看单 Pod,再上聚合平台看全局趋势。
# 查看 Pod 当前日志
kubectl logs <pod-name> -n <namespace> --tail=200
# 查看上一个已崩溃容器的日志(排障必备)
kubectl logs <pod-name> -p -n <namespace>
容器化部署与日志挂载的完整实践见 Docker 编排实战。把”本地看一眼”升级为”平台统一看”,是日志分析从手工迈向工程化的关键一步。
十、小结
Web 服务器日志分析不需要昂贵的平台,从一行 awk 到 GoAccess 仪表盘,再到定时自动化,是一条平滑的进阶路线。关键是养成”每天看一眼日志”的习惯——绝大多数线上故障和安全事件,都能在日志里找到最早的征兆。把今天的命令存进你的工具箱,下次排障你会感谢现在的自己。




