copytruncate是少数能“无感轮转”日志的方案,适用于不响应信号、日志句柄长期持有且不可停机的场景;必须配置在具体日志路径块内,独占一行,不配create,禁用postrotate;高频或大日志下存在丢失与i/o压力风险,建议用size+delaycompress缓解。

当应用不响应 SIGHUP 或其他重载信号、日志文件句柄长期被持有、又不能停机时,copytruncate 是少数能“无感轮转”的方案——但它不是万能解药,用对场景、控住风险,才真正有用。
哪些应用必须依赖 copytruncate?
满足以下任一条件,就别强求重命名+信号模式,直接上 copytruncate:
- 程序启动后 从不关闭日志文件描述符,比如某些嵌入式守护进程、旧版 MySQL 错误日志、自研 C 服务
- 日志由多个进程通过
>>追加写入,无法统一控制 reopen - 运行在容器中,且未暴露信号接收能力(如 Dockerfile 没配
signal: SIGUSR1,或 init 进程屏蔽了信号) - 你无法验证或修改程序源码,又不敢贸然重启——这时
copytruncate是最稳妥的兜底选择
怎么配置 copytruncate 才真正生效?
它必须写在具体日志路径的配置块内,不能放在全局段。示例:
/var/log/app/big.log {
daily
rotate 30
compress
missingok
notifempty
copytruncate
}
关键细节:
-
copytruncate独占一行即可,无需参数,位置靠后更易识别 - 不用配
create—— 它不新建文件,而是清空原文件内容 - 保留原文件的 inode、权限、属主,监控脚本或审计工具若依赖路径或 inode,不会中断
- 不要和
postrotate混用——它不需要通知程序,加了也白加
高频写入或大日志下必须防的坑
copytruncate 的本质是“复制 + 截断”,中间有毫秒级窗口,高并发或大日志会放大风险:
- 日志丢失:复制完成前新写入的内容,可能既没进归档文件,又被截断清空——尤其在每秒写入数百 KB 的服务中较常见
- I/O 压力尖峰:一个 800MB 的日志轮转,会触发约 1.6GB 磁盘读写(复制一次 + 清空一次),避开业务高峰时段执行
- 超大文件慎用:单个日志达 2GB 以上时,复制可能耗时数秒,期间磁盘延迟升高,影响同盘数据库等关键服务
缓解建议:改用 size 100M 替代 daily,把单次操作量压到可控范围;搭配 delaycompress 避免压缩与复制争抢 I/O。
比 copytruncate 更稳的替代思路
如果丢日志不可接受,优先考虑这些折中路径:
- 为应用加一层轻量 wrapper:捕获
SIGUSR1后主动调用fclose()+fopen(),再透传信号给原进程 - 用
systemd的ExecReload=定义标准重载逻辑,配合logrotate的postrotate systemctl reload app.service - 对容器化服务,在入口脚本中监听
/dev/stdout写入,由宿主机采集器(如 Filebeat)接管轮转,绕过容器内 logrotate











