在Nginx里做HTTP/2推送调试时,有一个很容易被忽略的细节:push日记日志文件会随着时间迅速堆积。如果只依赖系统自带的服务,旧文件不会被自动删除,磁盘空间迟早会出问题。要解决这个事,一部分定制化的Nginx模块提供了一个配置项,叫做 http2_push_diary_log_max_files,专门用来限制这类日记文件的最大保留数量。

这个参数理解起来不复杂,但真正落到配置里还是有很多细节需要注意。下面从它的作用、切分策略、与logrotate的配合以及常见错误几个角度分别说明。
一、这个参数到底控制什么
Nginx原生只有 access_log 和 error_log 两个日志记录能力,它们不会自动按数量清理历史文件。对于HTTP/2 push产生的业务日记,很多团队会在Nginx上扩展一个记录模块,把每次服务端推送的资源、客户端是否接收、连接是否被重置等信息写入独立文件。http2_push_diary_log_max_files 就是这个扩展模块提供的参数之一,用于告诉模块在push_diary日志目录中最多保留多少份文件。
该参数的值是一个正整数。比如设为14,表示保留最近14份文件。它不关心单份文件的大小,也不关心文件是按天还是按小时切割,只负责数量上限。当第15份文件即将生成时,模块会先找出最旧的一份并删除。如果日志目录中还有其他业务文件混杂,模块可能误判,所以建议为这类日志单独建立目录。
需要特别说明的是,该指令并不是Nginx官方文档中收录的标准指令,常见于内部定制的分支、第三方模块或者团队自行编译加入的功能。因此配置之前要先用 nginx -V 2>&1 | grep http2_push 确认模块是否已经加载,否则Nginx会直接报unknown directive错误。
http {
server {
listen 443 ssl http2;
server_name ipipp.com;
# 控制HTTP/2 push日记日志的最大文件数
http2_push_diary_log_max_files 14;
location / {
http2_push /style.css;
http2_push /app.js;
access_log logs/push_diary.log combined;
}
}
}
二、配置方式和文件命名约定
想让这个参数稳定工作,必须配合一套规律的文件命名规则。假设当前正在写入的文件始终叫 push_diary.log,每次切割后重命名为 push_diary_YYYYMMDD.log。模块通过扫描目录或维护索引表来统计符合规则的归档文件数量。不要直接给日志文件加上随机后缀,否则会出现扫不到或数量统计错误的问题。
一个典型的Nginx配置可以这样写:把 http2_push_diary_log_max_files 放在server块中,同时把 access_log 指向专门的push日志目录。这样模块在初始化时会读取该目录下的归档文件列表,并根据最大保留数量决定是否需要删除最旧的记录。
需要注意的是,该指令的生效上下文可能是http或server,取决于模块实现。放在location中不一定能识别。要参考该模块的说明文档,并且改完配置先用 nginx -t 做语法检查,再平滑reload。不要在日志目录里放其他无关键文件,避免误删和数据混淆。
三、用脚本或logrotate完成实际轮转
Nginx自己不会主动切割日志,即使配置了数量上限,也不会自动创建新文件。常见的做法是每天夜间把当前文件重命名,然后给Nginx进程发送USR1信号,让它重新打开日志文件。如果没有这一步,Nginx仍然会往旧文件句柄写入,磁盘占用继续增长,而新名字的文件不会产生。
下面这个脚本展示了按日期切割并清理超量文件的过程。它把 http2_push_diary_log_max_files 的值设置为14后,同时让脚本也只保留14份归档,两边保持一致。
#!/bin/bash LOG_DIR=/var/log/nginx/push MAX_FILES=14 # 按日期重命名当前日志 mv "$LOG_DIR/push_diary.log" "$LOG_DIR/push_diary_$(date +%Y%m%d).log" # 发送USR1信号让Nginx重新打开日志文件 kill -USR1 $(cat /var/run/nginx.pid) # 清理超过保留数量的历史文件 ls -1t "$LOG_DIR"/push_diary_*.log | tail -n +$((MAX_FILES + 1)) | xargs rm -f
如果服务器已经有logrotate,直接使用它的 rotate 指令会更省心。配置如下:
/var/log/nginx/push/push_diary.log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 $(cat /var/run/nginx.pid)
fi
endscript
}
logrotate的 rotate 14 和模块内部的 http2_push_diary_log_max_files 14 最好保持一致。如果logrotate保留14份,而模块只保留7份,删除动作就会提前发生,可能导致你希望保留的旧日志被过早清掉;反过来,模块不删但logrotate删除,也可以达到目的,但模块的记录索引可能不会及时更新。二者协同工作才能让日志数量始终稳定在预期范围内。
四、常见错误与排查思路
第一个常见错误是忘了重新打开日志文件。很多人在shell里只执行mv,不发送USR1信号,结果第二天发现 push_diary.log 还是旧文件,归档目录里啥也没有。正确的顺序是先mv,再 kill -USR1。如果Nginx运行在容器中,还要注意pid文件路径是否正确。
第二个问题是路径包含空格或反斜杠。Linux下一般不会有问题,但如果Nginx跑在Windows上,日志路径可能是 C:\nginx\logs\push_diary.log。在配置文件里如果遇到反斜杠转义问题,需要写全反斜杠,例如 C:\\nginx\\logs\\push_diary.log。反斜杠不是装饰,少了它路径会直接失效。目录权限也需要注意,Nginx删除旧文件时需要有删除权限,日志目录不能被设成只读。至少Nginx运行用户要对该目录有写权限,否则模块会在写新文件时失败,错误日志里出现Permission denied。
最后一个排查思路是合理设置保留数量。不要把这个值设置过小,调试阶段建议至少保留7到14份,避免问题还没复现日志就已经被清空。如果发现日志数量没有按预期减少,可以先检查模块是否真正加载,再确认归档文件命名是否符合模块扫描规则,最后查看Nginx错误日志是否有权限或路径相关的报错。

