Nginx 日志系统
日志是 Nginx 的"黑匣子"——出了问题、性能瓶颈、攻击溯源,全靠它。这一章我们系统讲清 Nginx 的两种日志:access_log(访问日志)和 error_log(错误日志),以及如何自定义日志格式、按需关闭、自动切割,把日志从"占满磁盘的负担"变成"生产可观测的金矿"。
1. 两种日志:访问 vs 错误
Nginx 有两种日志,用途完全不同:
- access_log:记录每个 HTTP 请求(谁、什么时候、访问了什么、状态码、字节)。是分析流量、用户行为、攻击的依据。
- error_log:记录Nginx 自身的错误(启动失败、配置错误、上游超时)。是排查故障的依据。
默认位置:
/var/log/nginx/access.log— 访问日志/var/log/nginx/error.log— 错误日志
2. 默认的日志配置
Nginx 默认配置文件里就有日志相关指令:
# 默认的日志配置(在 http 块里)
http {
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
}
# 默认 access.log 每行一条记录,例如:
# 192.168.1.5 - - [05/Aug/2026:10:23:11 +0800] "GET /api/users HTTP/1.1"
# 200 1234 "https://google.com" "Mozilla/5.0 (Macintosh; ...)"注意 log_format 定义格式,access_log 引用格式名字(这里是 main)。多个 server 可以共用同一个格式,也可以各自定义。
3. 自定义 log_format:加耗时与上游信息
默认格式不够用——尤其做反向代理时,想知道请求总耗时、后端处理耗时。自定义 log_format:
# 自定义 log_format:加请求耗时、上游耗时、客户端真实 IP
http {
log_format detailed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time" '
'urt="$upstream_response_time"';
access_log /var/log/nginx/access.log detailed;
}
# 每个变量的含义:
# $remote_addr 客户端 IP(用了代理后是代理 IP)
# $remote_user HTTP Basic 认证用户名(通常为 -)
# $time_local 本地时间
# $request 完整请求行(GET /path HTTP/1.1)
# $status HTTP 状态码
# $body_bytes_sent 响应体字节数(不含头)
# $http_referer Referer 头
# $http_user_agent User-Agent
# $request_time 整个请求总耗时(秒)
# $upstream_connect_time 连接后端耗时(反向代理用)
# $upstream_response_time 后端处理耗时
# $http_x_forwarded_for XFF 头(拿真实客户端 IP)加了这些字段后,日志能直接拿来画"性能火焰图"——找出慢请求是网络慢、Nginx 处理慢,还是后端慢。
4. 每个 server 独立日志
多个站点混在一个 access.log 里很难分析。最佳实践:每个 server 一个独立日志:
# 每个 server 可以单独指定日志
server {
listen 80;
server_name api.example.com;
access_log /var/log/nginx/api.access.log main;
error_log /var/log/nginx/api.error.log warn;
}
server {
listen 80;
server_name static.example.com;
# 静态资源站访问量巨大,可以暂时关闭日志避免磁盘 I/O 爆炸
access_log off;
}
# 关闭健康检查日志(避免刷屏)
location /health {
access_log off;
return 200 "ok";
}
# 只记录错误请求(4xx/5xx)
location / {
access_log /var/log/nginx/error_only.log main if=$loggable;
}
# 配合 map:仅状态码 >= 400 时 $loggable 为 1
map $status $loggable {
~^[45] 1;
default 0;
}几个有用的小技巧:
- 静态资源站关掉 access_log:每秒上万次请求全记日志,磁盘 I/O 是灾难。
- 健康检查不记日志:K8s 的 liveness probe 每秒一次,会把正常请求淹没。
- 按状态码过滤:只记 4xx/5xx 错误,可以用
if条件控制。
5. error_log 的级别
error_log 有 8 个级别,从最详到最略:
# error_log 的级别(从详到略)
error_log /var/log/nginx/error.log debug; # 调试(超详细,生产别用)
error_log /var/log/nginx/error.log info; # 信息
error_log /var/log/nginx/error.log notice; # 通知(默认)
error_log /var/log/nginx/error.log warn; # 警告
error_log /var/log/nginx/error.log error; # 错误
error_log /var/log/nginx/error.log crit; # 严重
# 生产推荐 warn 或 error 级别
# 排查问题时临时改 debug,但记得改回去!
# 可以写多个 error_log(输出到不同文件 + 不同级别)
error_log /var/log/nginx/error.log warn; # 完整 warn 以上
error_log /var/log/nginx/critical.log crit; # 只记 crit(给告警系统)生产环境推荐 warn。debug 级别会产生海量日志(且 Nginx 默认编译不支持 debug,需要 --with-debug 重编译)。排查问题时临时开 info 或 notice,排查完立刻改回 warn。
6. 日志切割:避免单个文件撑爆磁盘
高流量站点的 access.log 一天就能写满几个 GB——必须按天切割、压缩、删除老日志。Linux 标准做法是用 logrotate:
# /etc/logrotate.d/nginx —— 自动按天切割日志
/var/log/nginx/*.log {
daily # 每天切割
missingok # 日志不存在不报错
rotate 30 # 保留 30 天
compress # 切割后 gzip 压缩
delaycompress # 延迟一天压缩(最新的不压缩)
notifempty # 空文件不切割
create 640 www-data adm # 新日志文件的权限和属主
sharedscripts # 多个日志匹配时 postrotate 只跑一次
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
# 关键点:kill -USR1 让 Nginx 重新打开日志文件
# 不能用 HUP(那是 reload 配置)关键点:不能直接 mv 日志文件——Nginx 持有的文件描述符还指向老 inode,新请求会写到"已删除"的老文件里,磁盘空间也释放不掉。正确做法是发 USR1 信号让 Nginx 重新打开日志文件。
Ubuntu/Debian 装 Nginx 后会自动创建 /etc/logrotate.d/nginx,开箱即用。
7. 实时排查与日志分析
日志写出来是为了分析。一些常用的命令行技巧:
# 实时查看访问日志
sudo tail -f /var/log/nginx/access.log
# 统计访问量 TOP 10 IP
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
# 统计每个 URL 的访问次数
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 找出所有 404 请求
awk '$9 == 404 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# 找出慢请求(耗时 > 1 秒)
awk '{if ($NF > 1) print $0}' /var/log/nginx/access.log
# 统计状态码分布
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# 更专业的工具:GoAccess、AWStats、ELK当数据量大了,命令行不够用,需要专业工具:
- GoAccess:终端实时分析,生成漂亮报告。
- ELK(Elasticsearch + Logstash + Kibana):把日志汇总到 ES,可视化分析。
- Loki + Grafana:云原生时代的轻量级日志方案。
- 阿里云 SLS / 腾讯云 CLS:托管日志服务,免运维。
8. 常见坑点
- 日志时间不对:默认是本地时间,K8s/Docker 容器里可能是 UTC,注意时区。
- $remote_addr 是代理 IP:用了反向代理后,要拿真实 IP 需要配
set_real_ip_from+real_ip_header X-Forwarded-For。 - 日志文件被删但空间没释放:Nginx 还在写老 inode。重启 Nginx 或发 USR1 信号。
- $request 显示完整 URI 含查询参数:会泄露密码 token,敏感参数要脱敏。
- access_log 关不掉:检查上层 http 块是否强制开启了。
9. 一个生产可用的日志配置
整合本章内容,下面是一个推荐的日志配置:
- log_format 包含耗时字段(request_time、upstream_response_time);
- 每个 server 一个独立 access.log;
- error_log 用 warn 级别;
- 静态资源和健康检查关掉 access_log;
- logrotate 按天切割,保留 30 天。
把它作为团队标准,让所有项目都用同一套日志规范——后续做监控、告警时格式统一是基础。
小结
日志是可观测性的基石。这一章你学会了 access_log 与 error_log、log_format 自定义、按 server 隔离、logrotate 切割、命令行分析。下一篇我们讲 Nginx 的性能调优——把硬件压榨到极致。
← 上一篇 gzip 压缩
下一篇 性能调优 →