日志滚动是用来避免日志文件过大吃满硬盘的情况。对于已经内置了日志滚动能力的程序(如 Django 的 logging 可以做滚动),不在此文讨论范围内。对于没有内建日志滚动的程序,可能有这几个场景需要做日志滚动:
- 对于常驻的、不停打日志的程序
- 对于非常驻的程序
- 将标准输出重定向到文件并需要日志滚动
对于常驻的程序,如果想做日志滚动,需要程序支持重新打开日志文件,以便在老文件做完操作后,程序可以在新文件打日志。比如 Gunicorn 就支持接收 USR1 信号来 重新打开日志。
除了标准输出的场景,其他两个可以 logrotate
工具来做。
logrotate
Linux 上有个内置的工具,logrotate
,可以用来做 log rotation。man logrotate
可以看文档。这个是 1990 年就存在的项目,至今还在更新。
logrotate
需要你提供一个配置文件,指导它怎么做 rotation。这个配置文件语法古老,多看看 manpage 再下手。
sharedscripts
sharedscripts
有用,比如你的 Gunicorn 会有两个文件 access.log
, error.log
。你这样写:
/path/to/access.log /path/to/error.log {
rotate 5
daily
sharedscripts
postrotate
/bin/kill -USR1 `cat /path/to/gunicorn.pid`
endscript
}
此时就算 access.log
, error.log
同时被 rotate,也只会调用一次 kill
命令。不带 sharedscripts
则会被调用两次。
运行方式
命令行:
logrotate -s <path-to-logrotate.status> <path-to-logrotate.conf>
可以搭配 crontab 或者 Systemd Timer。
内部原理
logrotate
在每一次运行时,会将它将要做 rotate 的文件时间,写入 /var/lib/logrotate.status
(默认位置)文件中,如:
logrotate state -- version 2
"/usr/local/services/radio_api_server-1.0/log/access.log" 2018-1-3
"/usr/local/services/radio_api_server-1.0/log/error.log" 2018-1-3
如果你的文件第一次被 logrotate
遇到,那么除非 size
达到要求被 rotate,否则用 daily
, weekly
, monthly
这些是不会做 rotate 的,只会将这个文件的信息纪录进 logrotate.status
中。logrotate
在判断时,只看这个 status 文件,并不在乎文件本身的 Access Time / Modified Time 等等。
这导致你写 crontab 时要注意时间。比如你想做一个 daily 的 rotation,写的要求每天 23:59 分执行。但是 crond 可能到了次日 0 点才执行。这时候 rotation 出来的文件列表,可能会少一天:
access.log-20180101.gz
access.log-20180102.gz
access.log-20180104.gz # 0103 那天 23:59,没来得及执行 logrotate,导致备份这里的日期是 4 号的
这时候,4 号 23:59 分执行的 logratote
,就不会对 access.log
做 ratation 了。
对标准输出做日志滚动
需要在程序启动时将标准输出重定向到其他工具上,再打到文件上。
SuperUser 上这篇 帖子 给出了非常多的工具参考。