copytruncate必须写在具体日志路径的配置块内,独占一行且不带参数;放全局段无效,与create冲突,会丢失毫秒级日志,需配合size和delaycompress缓解风险。

copytruncate 必须写在日志路径块内部,不能放全局段
logrotate 的 copytruncate 是一个**作用域敏感**的指令:它只对紧邻其上方的路径块生效,且必须独占一行、不带参数。放在 /etc/logrotate.conf 全局段或 include 之外的位置,完全无效。
常见错误是把它写成全局默认行为,比如:
/etc/logrotate.conf(错误示范) copytruncate daily rotate 7 ...
这样配置后,所有日志仍走默认重命名流程。正确写法只能是:
/opt/app/logs/app.log {
daily
rotate 7
compress
missingok
notifempty
copytruncate
}
注意:copytruncate 后面不要加冒号、等号或任何值;它本身就是一个布尔开关,出现即启用。
copytruncate 和 create 冲突,配了就白配
copytruncate 的本质是“复制内容 → 截断原文件”,整个过程不删除也不重建文件,因此 create 指令对它完全无意义——它不会创建新文件,也不会改权限或属主。
如果你同时写了 create 0644 appuser appgroup 和 copytruncate,logrotate 会静默忽略 create,但原日志文件的权限/属主保持不变。这会导致两个问题:
- 若原日志属主是
root,而应用以普通用户运行,后续写入会因权限拒绝失败 - 若原日志权限是
0600,但你期望其他用户可读,create不生效,就得手动chown/chmod一次
解决办法只有两个:
• 首次部署前,确保日志文件已由应用自身或初始化脚本以正确权限/属主创建(推荐)
• 或者不用 copytruncate,改用 postrotate + 信号方式(前提是应用支持)
copytruncate 会丢日志,高频写入场景必须加 size + delaycompress
copytruncate 在“复制完成”和“截断开始”之间存在毫秒级窗口,期间新写入的日志行可能被直接覆盖或丢失。这个风险在以下情况会被放大:
- 日志文件 >100MB,复制耗时明显
- 应用每秒写入数百行以上(如 API 网关、实时采集服务)
- 磁盘 I/O 已接近瓶颈(复制动作进一步抢占带宽)
缓解策略不是禁用它,而是控制触发时机:
• 改用 size 50M 替代 daily:避免单次复制过大文件
• 加 delaycompress:让压缩延后到下一轮,减少轮转时的 I/O 峰值
• 配合 minsize 10M:防止空转或小文件频繁触发
示例:
/var/log/collector/output.log {
size 50M
minsize 10M
rotate 14
compress
delaycompress
missingok
notifempty
copytruncate
}
验证 copytruncate 是否真生效,别只看文件名
很多人看到 app.log.1 生成、app.log 变空,就以为成功了。但关键要看 inode 是否一致——因为 copytruncate 的核心价值就是保 inode。
执行轮转后,立刻检查:
ls -i /opt/app/logs/app.log /opt/app/logs/app.log.1
如果两个文件的 inode 编号相同,说明没生效(那是重命名残留);
如果 app.log 的 inode 和轮转前一致,而 app.log.1 是全新 inode,才说明 copytruncate 正确触发。
另一个硬指标是进程句柄:lsof -p $(pgrep -f "myapp") | grep app.log
输出中应始终显示同一个 inode,且文件大小在轮转后从 0 开始增长。
最容易被忽略的是:某些监控工具(如 filebeat、rsyslog input_file)依赖 inotify 监听文件变更,而 copytruncate 不触发 IN_MOVED_FROM/IN_MOVED_TO 事件,只触发 IN_MODIFY —— 如果你的采集端没适配这种模式,会漏掉截断瞬间之后的第一批日志。

















