Nginx 日志系统

日志是 Nginx 的"黑匣子"——出了问题、性能瓶颈、攻击溯源,全靠它。这一章我们系统讲清 Nginx 的两种日志:access_log(访问日志)和 error_log(错误日志),以及如何自定义日志格式、按需关闭、自动切割,把日志从"占满磁盘的负担"变成"生产可观测的金矿"。

1. 两种日志:访问 vs 错误

Nginx 有两种日志,用途完全不同:

默认位置:

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;
}

几个有用的小技巧:

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

当数据量大了,命令行不够用,需要专业工具:

8. 常见坑点

9. 一个生产可用的日志配置

整合本章内容,下面是一个推荐的日志配置:

把它作为团队标准,让所有项目都用同一套日志规范——后续做监控、告警时格式统一是基础。

小结

日志是可观测性的基石。这一章你学会了 access_log 与 error_log、log_format 自定义、按 server 隔离、logrotate 切割、命令行分析。下一篇我们讲 Nginx 的性能调优——把硬件压榨到极致。

← 上一篇 gzip 压缩

下一篇 性能调优

✈️💬