在运维或开发过程中,服务运行久了ref="/tag/415/" style="color:#479099;font-weight:bold;">日志文件会越来越大。比如一个 Web 服务器连续跑几天,日志可能就膨胀到几个GB。这时候就需要做日志轮转(log rotation),把旧日志归档,腾出空间给新日志用。
常见的轮转方式是直接移动(rename)日志文件,然后通知进程重新打开日志。但问题来了:有些程序不会监听文件变化,你一改名,它还在往原来的文件描述符写,结果新日志就“消失”了——其实是写到了被移动的文件里。
copytruncate 的作用
为了解决这个问题,logrotate 提供了一个叫 copytruncate 的选项。它的意思是:不重命名原文件,而是先复制一份当前日志内容到归档文件,然后清空原日志文件。
这样做有个明显好处:原始日志文件的 inode 和名字都没变,程序继续往里面写完全不受影响,不需要发 HUP 信号重启进程,也不用担心日志丢失。
怎么用 copytruncate
在 logrotate 配置里加上这一行就行:
/var/log/myapp.log {
daily
rotate 7
copytruncate
compress
missingok
}
上面这段配置的意思是:每天轮转一次 /var/log/myapp.log,保留7份,使用 copytruncate 方式处理,压缩归档,并且即使日志不存在也不报错。
潜在风险要注意
虽然方便,但 copytruncate 有坑。在“复制”和“清空”之间存在时间差,如果程序正在写日志,可能会丢掉这期间产生的几行。对日志完整性要求高的场景,比如审计、安全监控,就得慎用。
举个例子,你家楼下便利店用系统记流水,如果正好在清空日志那瞬间有一笔扫码支付进来,这条记录可能就没了。虽然概率小,但真出了问题不好查。
所以,能支持 postrotate 脚本通知进程重载的,优先用 rename + signal 的方式。实在没法发信号,再考虑 copytruncate。