copytruncate本质是为无法重载配置的老旧服务提供伪轮转:先复制日志内容再清空原文件,保持文件句柄不变;但存在末尾日志丢失、inode占位不释放及写入覆盖风险,属兜底方案而非最佳实践。

Logrotate 的 copytruncate 选项,本质是为无法重载配置或不支持 SIGHUP 的老旧/封闭服务(如某些 Java 应用、自研守护进程)提供一种“伪轮转”方案:它不移动原日志文件,而是先复制内容、再清空原文件,让服务继续往同一路径写入,避免因文件句柄丢失导致日志丢失或写入失败。
copytruncate 的工作原理与适用边界
Logrotate 默认通过 rename(mv)实现轮转,依赖服务能响应信号关闭旧文件句柄并打开新文件。而 copytruncate 绕过这一步:它先用 cp 复制当前日志内容到归档路径,再用 truncate -s 0 或 > file 清空原始文件。服务因仍持有原文件的 inode 句柄,写入不受影响。
但要注意三点限制:
- 复制过程期间新日志会持续追加,
cp只能捕获执行瞬间的内容快照,可能丢失少量末尾日志(毫秒级,通常可接受) - 清空操作(truncate)要求服务以
O_APPEND模式打开文件;若服务使用固定偏移写入(如某些数据库日志),可能覆盖已有内容 - 原日志文件大小归零,但磁盘空间不会立即释放——只有所有进程释放该 inode 的句柄后,空间才真正回收;若服务长期不重启,已删除但未释放的“幽灵文件”会持续占位
正确配置 copytruncate 的关键项
在 /etc/logrotate.d/your-app 中,必须显式声明 copytruncate,并搭配合理策略防止误操作:
-
禁用 create:不要加
create指令,否则 Logrotate 会尝试重建文件,破坏原有权限和 inode 关联 -
慎用 delaycompress:若启用压缩,需确认压缩工具(如 gzip)不会因读取中被 truncate 而出错;建议搭配
compress+delaycompress并测试 -
设置 size 或 daily 配合 maxage/maxsize:避免单个日志无限增长,例如
size 100M或daily+maxage 30 - 添加 missingok:防止服务尚未启动时 Logrotate 报错中断
示例配置:
/var/log/myapp/app.log {
daily
missingok
rotate 30
compress
delaycompress
copytruncate
notifempty
}
替代方案对比:什么情况下不该用 copytruncate
如果服务本身支持标准日志重载机制,优先采用更健壮的方式:
-
发送 SIGHUP:如 Nginx、rsyslog 支持,配合
postrotate ... kill -HUP `cat /var/run/service.pid` ... endscript -
使用管道+logger:启动时将 stdout/stderr 接入
logger -t myapp,由 rsyslog 统一管理轮转,完全解耦应用逻辑 -
应用内集成 logrotate 兼容库:如 Java 的 Logback 提供
TimeBasedRollingPolicy,自动按时间切分,无需外部干预
copytruncate 是兜底手段,不是最佳实践。它解决的是“现在就得上线、代码不能改、服务不能停”的现实约束。
验证与排障要点
部署后务必验证行为是否符合预期:
- 手动触发:
logrotate -d /etc/logrotate.d/your-app(-d 查看调试输出,不实际执行) - 观察 inode 变化:
ls -i /var/log/myapp/app.log轮转前后应保持相同 inode 号 - 检查空间释放:
lsof +L1查看是否有已删除但仍被占用的日志文件(output 中含deleted字样) - 监控日志完整性:比对轮转前最后几行与新文件开头,确认无截断或重复
若发现日志丢失频繁,说明服务写入速率极高或磁盘 I/O 延迟大,应考虑改用支持信号重载的服务架构,或引入异步日志收集器(如 Filebeat)做缓冲转发。


















