必须用 copytruncate 的情况是:程序长期持有日志文件句柄、多进程写入、容器中无法接收信号或不确定是否支持重载。它通过复制后截断实现无中断轮转,但存在丢日志、I/O 压力大等风险。

直接用 copytruncate 指令,就能让 logrotate 在不中断服务、不依赖程序配合的前提下完成日志轮转。它特别适合那些无法响应信号(如 SIGHUP、USR1)、日志文件句柄长期被持有、或运行在容器中收不到外部信号的老应用。
什么情况下必须用 copytruncate?
当你的应用满足以下任一条件时,默认轮转方式会失效,copytruncate 就成了刚需:
- 程序启动后一直打开日志文件,从不关闭或重新 open —— 比如某些嵌入式服务、旧版 MySQL 错误日志、自研守护进程
- 日志文件被多个进程写入,或通过重定向(
>>)持续追加,无法统一控制重开 - 运行在容器里,且未暴露信号接收能力(如没有配置
signal: SIGUSR1),logrotate 发送的 kill 信号根本无效 - 你不确定程序是否支持日志重载,又不想冒险重启或修改其配置
怎么配置 copytruncate?
在 logrotate 配置块中加入 copytruncate 即可,无需额外参数。注意它必须和日志路径配对使用,不能放在全局段。
示例(针对一个老旧监控代理的日志):
/var/log/monitor/agent.log {
daily
rotate 14
compress
missingok
notifempty
copytruncate
}
关键点说明:
-
copytruncate必须独占一行,位置无严格要求,但建议放在靠后便于识别 - 不用配
create或postrotate—— 它不创建新文件,也不需要通知程序 - 保留原文件 inode 和权限,监控工具或审计脚本若依赖文件路径或 inode,不会中断
要注意哪些实际风险?
copytruncate 不是银弹,用之前得清楚它的代价:
- 可能丢日志:在“复制内容”到归档文件和“截断原文件”之间存在毫秒级窗口,此时新写入的内容会丢失。高并发写入场景需评估容忍度
- I/O 压力大:每次轮转都要完整读取当前日志再写一遍,一个 500MB 的日志会触发约 1GB 的磁盘 I/O。避免在业务高峰时段轮转
- 不适用于超大日志:若日志单个文件达数 GB,复制操作可能耗时数秒,期间磁盘延迟升高,影响同盘其他服务
有没有更稳妥的替代方案?
如果 copytruncate 的风险不可接受,可以考虑这些折中做法:
- 改用
size触发轮转(如size 100M),把单次复制量控制在可接受范围 - 搭配
delaycompress,避免压缩与复制同时发生,减轻 I/O 峰值 - 对关键服务,优先尝试让程序支持日志重载:比如加一层
exec wrapper捕获信号并调用kill -USR1 $PID,比 copytruncate 更可靠 - 容器环境推荐用
stdout/stderr输出 + 日志驱动(如json-file或fluentd),由平台统一做轮转,绕过文件级操作


















