Carry の Blog Carry の Blog
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • OpenClaw
  • Hermes Agent
  • Claude Code
  • MySQL8-SOP手册
  • MySQL实战45讲学习笔记
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Carry の Blog

好记性不如烂键盘
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • OpenClaw
  • Hermes Agent
  • Claude Code
  • MySQL8-SOP手册
  • MySQL实战45讲学习笔记
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 工作笔记

  • K8S

  • Nginx

    • Nginx 运维知识地图:从配置基础到反向代理实战
    • Nginx 给内部服务加认证:Basic Auth / LDAP / OIDC 三档怎么选
    • 利用nginx+sftp实现一个可供用户下载的服务
    • nginx配置文件及模块
    • nginx通过四层代理实现端口转发
    • NGINX基于cookie针对同一域名进行分流转发
    • nginx利用内置模块配置限速限流
    • 利用NGINX内置模块mirror进行流量复制等操作
    • 使用$remote_user字段记录访问NGINX的用户
    • Nginx 日志切割两种方案对比:外部脚本 vs 配置文件原生切割
      • 1. 方案一:外部脚本 + logrotate 思路(传统部署)
      • 2. 方案二:Nginx 配置文件原生切割(容器化部署更友好)
        • 结合自定义日志格式,按域名+小时切分
      • 3. 该选哪种
      • 4. 可复用要点
    • nginx配置微信小程序校验及其他
    • nginx配置gzip压缩
    • 由Nginx集中代理分散的PHP集群的实践
    • http状态码详解
    • OpenResty-1-13-6-2-新增ldap模块儿
    • 排查NGINX的open_file_cache导致发布后访问404的问题
    • 制作OpenResty-1-19-9-1的RPM包
    • Nginx Proxy Manager 使用笔记
    • Nginx 的端口复用:提升服务器并发能力
  • 监控

  • 网络安全

  • 其他

  • Docker

  • Linux笔记
  • Nginx
Carry の Blog
2018-06-26
目录

Nginx 日志切割两种方案对比:外部脚本 vs 配置文件原生切割收藏(二丫讲梵)备查

版本说明

以下配置在 Nginx 1.18+ 验证有效,$time_iso8601 等内置变量多年未变,可用于任意近期版本。

Nginx 的访问日志和错误日志默认不会自动切割,放任不管一天天变大,等到几个 G 才发现,排查问题时打开都费劲。切割日志有两种思路,选哪种取决于 Nginx 是怎么部署的。

# 1. 方案一:外部脚本 + logrotate 思路(传统部署)

如果 Nginx 是直接跑在宿主机上的传统部署方式,最简单的做法是写一个脚本,配合 cron 定时执行:

#!/bin/bash
Date=`date -d '-1 day' '+%Y-%m-%d'`
cd /var/log/nginx && mkdir logs/$Date
for i in access.log error.log
do
    gzip -c $i > logs/$Date/"$i"_"$Date".gz
    echo " " > $i
    find logs/ -ctime +30 | xargs rm -rf
done
1
2
3
4
5
6
7
8
9

这个脚本做三件事:把当前日志压缩归档到按日期命名的目录,清空原日志文件(echo " " > $i 而不是删除文件——删除会导致 Nginx 仍然写向已被删除的 inode,日志实际上并未真正切换),再清理 30 天前的旧归档。

坑:echo " " > $i 只是清空文件内容,Nginx 进程持有的文件描述符没有改变,这种"清空法"对追加写入(open 一次、之后一直 write)的日志是安全的;但如果用 mv/rm 之后新建同名文件,Nginx 还在往旧 inode 写,新文件永远是空的,必须额外给 Nginx 发 USR1 信号让它重新打开日志文件(这也是标准 logrotate + nginx 配置里 postrotate 阶段做的事)。

这个方案的局限:如果 Nginx 是容器化部署、日志只是 volume 挂载出来的,logrotate/外部脚本切割时还得想办法给容器内的 Nginx 进程发平滑重载信号,跨容器发信号并不直接,操作起来不优雅。

# 2. 方案二:Nginx 配置文件原生切割(容器化部署更友好)

容器场景下更干净的做法是完全不依赖外部工具,用 Nginx 自己的变量在配置层面直接生成按时间分文件的日志路径——这样每次日志滚动本质上就是"换了个文件名",不存在文件描述符失效的问题。

按天切割:

if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})") {
    set $year $1;
    set $month $2;
    set $day $3;
}
access_log /var/log/nginx/$year-$month-$day-access.log;
1
2
3
4
5
6

基于内置变量 $time_iso8601(ISO8601 格式时间戳)用正则捕获出年月日,拼进 access_log 的文件路径里——Nginx 每次写日志时按当前请求的时间戳决定写入哪个文件,日期变化时自动"切"到新文件,全程不需要重载进程。

如果想切得更细(比如按小时甚至按秒),把正则和变量都扩展到时分秒:

if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})") {
    set $year $1;
    set $month $2;
    set $day $3;
    set $hour $4;
    set $minutes $5;
    set $seconds $6;
}
access_log /data/log/test/nginx-access-$year-$month-$day-$hour-$minutes-$seconds.log json;
1
2
3
4
5
6
7
8
9

坑:if 指令在 Nginx 配置里非常受限(俗称"if is evil"),这里的用法只能放在 server 块内,不能直接写在 http 全局块,否则会报配置解析错误。

# 结合自定义日志格式,按域名+小时切分

多域名共用一个 Nginx 实例时,可以把日志格式定义抽成公共文件,配合按小时切割,方便按站点单独归档分析:

$ vim /usr/local/nginx/conf/log_format.conf
log_format json escape=json '{"remote_addr": "$remote_addr",'
                                 '"@timestamp": "$time_iso8601",'
                                 '"request_uri": "$request_uri",'
                                 '"verb": "$request_method",'
                                 '"httpversion": "$server_protocol",'
                                 '"response": "$status", '
                                 '"body_bytes_sent": "$body_bytes_sent", '
                                 '"referrer": "$http_referer", '
                                 '"user_agent": "$http_user_agent", '
                                 '"http_x_forwarded_for": "$http_x_forwarded_for", '
                                 '"server_name": "$host",'
                                 '"request_time": "$request_time",'
                                 '"upstream_response_time": "$upstream_response_time",'
                                 '"realpath_root": "$realpath_root",'
                                 '"request_body": "$request_body",'
                                 '"nginx_version": "$nginx_version",'
                                 '"scheme": "$scheme"}';
if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})") {
        set $year $1;
        set $month $2;
        set $day $3;
        set $hour $4;
}
access_log /home/nginx/logs/${server_name}-${year}-${month}-${day}-${hour}_access.log json;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

再在具体 server 块里引用:

server {
    listen       80;
    server_name  example.com;
    charset utf-8;
    include log_format.conf;
    location / {
        try_files /_not_exists_ @backend;
    }
    location @backend {
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host            $http_host;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_pass http://<PUBLIC_IP>:8180;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

${server_name} 变量让不同虚拟主机自动生成各自的日志文件,配合 json 格式方便直接喂给 ELK/Loki 这类日志系统解析。

验证:配置生效后如果日志没有新生成,先检查 Nginx 进程对目标目录是否有写权限,再实际发一次请求触发日志写入——nginx -t 语法检查通过不代表运行时权限没问题。

# 3. 该选哪种

外部脚本 + logrotate 配置文件原生切割
适用场景 传统宿主机部署 容器化 / 日志只是 volume 挂载
切割粒度 通常按天,改粒度要改脚本+cron 配置里改正则粒度即可到秒级
依赖 cron、外部脚本、给 Nginx 发信号 无外部依赖,Nginx 自身完成
归档/清理旧日志 脚本里顺手做(find -ctime 清理) 需要额外的清理机制(如日志系统的保留策略)

# 4. 可复用要点

  1. 判断依据只有一条:Nginx 是否容器化部署。宿主机部署选外部脚本方案更简单直接;容器场景选配置文件原生切割,避免跨容器发信号的麻烦。
  2. 外部脚本切割日志文件时,"清空文件内容"和"删除重建文件"效果完全不同——后者必须配合给 Nginx 发 USR1 重新打开日志句柄。
  3. Nginx 配置里的 if 指令能用的场景很受限,日志路径这种用法必须写在 server 块内。
#nginx
上次更新: 8/7/2026

← 使用$remote_user字段记录访问NGINX的用户 nginx配置微信小程序校验及其他→

最近更新
01
Browserless 使用笔记:把 Chrome 变成一个可以被并发调用的网络服务
08-07
02
ORDER BY 配合 LIMIT 触发的索引选择陷阱 原创
08-07
03
Nginx 运维知识地图:从配置基础到反向代理实战 原创
07-29
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式