ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux Nginx 怎么用管道配合 cronolog 实现日志自动切割

Linux Nginx 怎么用管道配合 cronolog 实现日志自动切割 前言先把标题里的前提说清楚access_log |/usr/sbin/cronolog ...这种管道写法在 Nginx 上不成立。CustomLog |/usr/bin/rotatelogs ...是 Apache HTTP Server 的特性Apache 会把日志行的写端接到一个子进程的 stdin 上。Nginx 从设计上就没走这条路——它的access_log指令的path参数只接受三种形态文件路径如/var/log/nginx/access.logsyslog:前缀如syslog:serverunix:/dev/log,facilitylocal7,tagnginxoff用于关闭日志。官方文档里没有|命令这一项。你把 Apache 的写法原样搬过来得到的只会是nginx -t报错或者 Nginx 老老实实去创建一个名叫|/usr/sbin/cronolog ...的普通文件路径合法时会真的建出来然后日志全塞进这个诡异文件名里。那按天切割日志这个真实需求怎么办Nginx 有它自己的三条路本文把它们分别讲清楚包括那条流传很广但坑很深的 FIFO 变通做法。以下基于 Debian 12 / nginx 1.22 与 RHEL 9 / nginx 1.24。access_log的syslog:支持需要 nginx 1.7.11请用nginx -v确认版本。一、三种可行方案对比方案原理优点代价logrotate USR1外部定时改名再让 Nginx 重开文件稳定、发行版标配、可控切割粒度最小到hourly轮转瞬间要重开文件syslog: rsyslogNginx 走 syslog 协议rsyslog 按模板写日期文件名原生支持、天然按日期分文件、可同时转发依赖 syslog 链路多一层配置丢失时不易察觉FIFO cronologNginx 写命名管道cronolog 读管道写日期文件文件名完全由你控制Nginx 阻塞风险、丢日志风险、非官方支持的用法推荐顺序就是表格顺序。下面逐个给出可运行配置。二、方案一logrotate USR1推荐这是发行版自带的做法也是最省心的。核心是轮转后给 master 发SIGUSR1等价于nginx -s reopen让它重新打开日志文件。# /etc/nginx/nginx.conf 片段 http { access_log /var/log/nginx/access.log main; server { listen 80; server_name site.example.com; access_log /var/log/nginx/site.access.log main; location / { root /srv/www; try_files $uri $uri/ 404; } } }# /etc/logrotate.d/nginx-daily /var/log/nginx/*.log { daily rotate 30 dateext dateformat -%Y%m%d missingok notifempty compress delaycompress create 0640 root adm # RHEL 9 请改成 create 0640 nginx nginx sharedscripts postrotate if [ -f /run/nginx.pid ]; then kill -USR1 $(cat /run/nginx.pid) fi endscript }演练与执行logrotate -d /etc/logrotate.d/nginx-daily # 只打印将要做的动作 logrotate -f /etc/logrotate.d/nginx-daily # 强制执行一次 ls -l /var/log/nginx/轮转后的文件名会是access.log-20260929、access.log-20260929.gz这种dateext的效果。如果你需要严格等于access_20260929.log在postrotate里补一步改名即可postrotate if [ -f /run/nginx.pid ]; then kill -USR1 $(cat /run/nginx.pid) fi for f in /var/log/nginx/*.log-2*; do [ -e $f ] || continue mv $f $(dirname $f)/$(basename $f .log- | sed s/$/.log/ ) done endscript这段mv只处理dateext生成的名字sed用于把access.log-20260929变成access.log这类形态实际命名请按你的规范自行调整改完先用logrotate -d演练一遍。三、方案二交给 syslog由 rsyslog 按日期落盘Nginx 原生支持把日志送到 syslog之后按日期分文件就是 rsyslog 的强项了。# Nginx 侧把 access_log 指向 syslog http { log_format main $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent; # facility 默认就是 local7tag 用来在 rsyslog 侧做匹配 access_log syslog:serverunix:/dev/log,facilitylocal7,tagnginx,severityinfo main; }syslog 套接字路径按发行版而定RHEL 系一般是 rsyslog 监听的/dev/logDebian 12 上/dev/log默认由 systemd-journald 提供装了rsyslog包之后由 rsyslog 接管同样是/dev/log。不确定时用下面的命令确认ss -lx | grep -E dev/log ls -l /dev/logrsyslog 侧按日期写文件# /etc/rsyslog.d/10-nginx.conf # 用动态文件名模板按年/月/日分文件$month 与 $day 均为两位补零 template(namenginxDaily typestring string/var/log/nginx/access_%$year%%$month%%$day%.log) if ($programname nginx) then { action(typeomfile dynaFilenginxDaily fileOwnernginx fileGroupnginx fileCreateMode0640) stop }systemctl restart rsyslog # 用 logger 验证模板是否生效facility 要对上 local7 logger -p local7.info rsyslog-daily-template-check ls -l /var/log/nginx/access_*.logif ($programname nginx)里的stop很关键没有它时同一条日志会被其它规则再写一遍到/var/log/messages。需要注意走 syslog 后日志格式不再由log_format全权决定syslog 头部时间戳、主机名、tag会由 syslog 链路加上log_format只决定消息体部分。四、方案三FIFO cronolog先读完风险再决定如果一定要文件名完全由 cronolog 的模板决定流传的做法是让 Nginx 写一个命名管道FIFO另起一个读端进程。# 1. 建管道 install -d -m 0755 /run/nginx-logs mkfifo -m 0620 /run/nginx-logs/access.fifo # 2. 装 cronolog先确认仓库里有没有 # Debian/Ubuntu: apt-get install -y cronolog # RHEL 系: dnf --enablerepoepel list cronolog # 仓库里没有就自行评估不要随手装来源不明的二进制 which cronolog# 3. Nginx 侧把日志指向这个 FIFO http { access_log /run/nginx-logs/access.fifo main; }# 4. 用 systemd 常驻读端/etc/systemd/system/nginx-log-feeder.service [Unit] Descriptionnginx access log feeder via cronolog Afternginx.service PartOfnginx.service [Service] Typesimple # 注意管道必须先有读端否则 Nginx 打开写端的 open() 会一直阻塞 ExecStart/bin/sh -c exec cronolog /var/log/nginx/access_%Y%m%d.log /run/nginx-logs/access.fifo Restartalways RestartSec1 [Install] WantedBymulti-user.target这一套能跑但你必须知道它为什么危险Nginx 打开 FIFO 写端时会阻塞。对命名管道调用open(O_WRONLY)会一直等到有读端打开为止。读端没起来时nginx -t或nginx -s reload就会挂在那里看起来像Nginx 卡死。因此必须先起读端或者保证读端永远在。读端一挂日志就静默丢失。读端进程退出后管道里的写端写不进去Nginx 侧只会往error.log记一条错误正常访问日志直接丢。超过PIPE_BUFLinux 上通常 4096 字节的一次写入不保证原子性。多 worker 同时写时超长的日志行会被交错切碎。常规 Access Log 行远小于 4KB但带超长User-Agent或 Referer 时会踩到。每个请求一次write()系统调用。直写文件时 Nginx 可以用buffer攒批走管道没有这个收益。这套用法不在 Nginx 官方文档的支持范围内。上线前必须在同版本的测试环境完整验证启动、重载、读端重启这三种场景。另外cronolog这个工具的版本长期停留在 1.6.2发行版里的包不一定都还维护着用之前先确认你的仓库里到底有没有、以及是不是你要的那一个。常见坑点❌ 照搬 Apache 的CustomLog |/usr/bin/rotatelogs ...或access_log |/usr/sbin/cronolog ...。 ✅ Nginx 的access_log不支持|命令。要么用 logrotate kill -USR1要么用syslog:。❌ 以为access_log /var/log/nginx/$time_iso8601.log能按日期自动分文件。 ✅access_log的路径不做变量展开这个变量会被当成字面文件名。❌ 轮转后只做了mv忘了让 Nginx 重开文件。 ✅ 用mv改名后Nginx 仍在往旧 inode 写。必须在postrotate里kill -USR1 $(cat /run/nginx.pid)。❌ 用copytruncate替代 USR1。 ✅copytruncate在复制完到截断之间存在窗口高并发下会丢掉这期间写入的行。USR1 才是给 Nginx 设计的做法。❌ 先启动 Nginx、后启动 FIFO 的读端。 ✅ 顺序反了会让 Nginx 卡在打开日志的阶段。要么先起读端要么干脆别用 FIFO 方案。❌ 在 rsyslog 规则里只写了action(...)没写stop。 ✅ 同一条日志会同时进/var/log/messages和你的日期文件既占空间又容易被误判为重复采集。❌ 日志目录放到/data/logs之类的位置配置正确却不出文件。 ✅ RHEL 系要处理 SELinux 标签semanage fcontext -a -t httpd_log_t /data/logs(/.*)?加restorecon -Rv /data/logs。❌ 只测了nginx -t就认为日志配置没问题。 ✅nginx -t只校验语法不校验运行期的路径权限、SELinux 上下文和写端是否存在。真验证要靠curl打一次请求再看文件里有几行。总结你的需求用什么关键动作按天切割最省心logrotatepostrotate里kill -USR1 $(cat /run/nginx.pid)按天切割且要集中采集syslog: rsyslogrsyslogtemplatedynaFile规则末尾加stop文件名模板必须自定义FIFO cronolog读端必须先起接受阻塞与丢日志风险高频切割每小时/每分钟logrotatehourlydateformat或直接上 syslog 交给 rsyslogApache 写法迁移—不存在对应写法需按 Nginx 的方式重写一句话结论Nginx 没有 Apache 那种日志管道。想要按日期分文件正规做法是 logrotate 轮转后让 Nginx 重开文件或者把日志交给 rsyslog 用动态文件名模板落盘|cronolog那条路在 Nginx 上要么写不出来要么靠 FIFO 变通并承担阻塞与丢日志的风险。
返回列表