登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

Linux logrotate 轮转后应用如何重新打开新日志文件

来源:17golang原创

时间:2026-09-13 11:01:37 344浏览 收藏

logrotate 轮转后,应用是否能写入新的 app.log,关键不在“文件名有没有回来”,而在进程持有的文件描述符是否已经从旧 inode 切换到新 inode。能响应 HUP 或提供 reload 接口的应用,应使用“重命名旧文件 + 创建新文件 + postrotate 通知”的方式;只有无法让应用关闭旧描述符时,才考虑 copytruncate

要点速览
  • create 配合普通重命名轮转,会生成新的 app.log;应用必须重新打开它。
  • postrotate 只负责执行通知脚本,不会替应用自动重开日志,HUP 或 reload 语义由应用决定。
  • stat 看 inode,再用 lsof 看进程 fd,才能确认应用没有继续写旧文件。
很多刚接触Linux日志运维的朋友都会碰到这个场景:配置好logrotate规则自动轮转切割日志之后,发现业务进程还在往已经被改名的旧日志文件里写内容,不会自动切到新生成的同名日志文件输出,下面我们就把常用的几种适配方案、优劣对比和落地要踩的注意点都说清楚。
直接用logrotate自带的copytruncate配置模式,或者给对应应用发USR1信号通知它重载日志句柄,是绝大多数场景下最稳妥的实现方式,不用重启业务进程也不会丢失正在写入的日志内容。

官方地址:https://github.com/logrotate/logrotate/

先分清移动文件和原地截断

普通轮转通常把原来的 app.log 改名为 app.log.1,然后按 create 规则建立一个同名新文件。此时目录里虽然重新出现了 app.log,但它已经是另一个 inode。应用如果一直持有旧文件描述符,日志仍会写进已经改名的 app.log.1

copytruncate 是另一条路:它复制内容后在原位置截断文件,应用不需要重开描述符,但官方手册明确提示复制与截断之间存在很小的数据丢失窗口。它适合无法被通知关闭日志的程序,不是“配置了就更安全”的默认答案。

方式应用是否要 reopen主要代价
rename + create需要依赖 HUP、reload 或重启语义
copytruncate不需要复制窗口可能丢少量日志

用 postrotate 让应用重新打开日志

把规则放在 /etc/logrotate.d/myapp,让轮转动作和通知动作处于同一个规则中。下面示例假定应用的 PID 文件是 /run/myapp.pid,并且约定收到 HUP 后重新打开日志;如果应用文档规定使用其他信号或自定义 reload 命令,应替换这一行。

/var/log/myapp/app.log {
    daily
    rotate 7
    missingok
    notifempty
    create 0640 myapp adm
    sharedscripts
    postrotate
        # 轮转完成后通知应用重新打开新的 app.log
        if [ -s /run/myapp.pid ]; then
            kill -HUP "$(cat /run/myapp.pid)"
        fi
    endscript
}

这里的顺序很重要:postrotate 在轮转后、压缩前执行,通知脚本失败时也可能影响后续处理。sharedscripts 适合一个规则匹配多个文件且只需通知一次的场景;单文件规则可以省略它。不要把 HUP 当成 Linux 对所有程序的统一“重开日志”接口,它只是发送信号,应用是否实现该语义要看自身文档。

Linux logrotate 配置示意图:postrotate 发送 HUP 后应用从旧 inode 重新打开新的 app.log
图1:logrotate 先生成新的 app.log,再由 postrotate 通知应用重新打开文件的操作示意图。

轮转后怎么确认已经写入新文件

先用调试模式查看规则是否匹配,再在维护窗口强制轮转。示例命令只用于读者在自己的主机上操作,输出中的 PID、inode 和路径应以现场值为准。

# 只展开规则,不真正修改文件
sudo logrotate -d /etc/logrotate.d/myapp

# 确认规则无误后,在维护窗口强制轮转一次
sudo logrotate -f /etc/logrotate.d/myapp

# 对照新旧日志的 inode;两个数字不同才说明生成了新文件
stat -c 'path=%n inode=%i size=%s' /var/log/myapp/app.log /var/log/myapp/app.log.1

# 查看应用当前打开的日志描述符,确认 fd 指向新 app.log
sudo lsof -p "$(cat /run/myapp.pid)" | grep '/var/log/myapp/app.log'

判断时不要只看文件大小:如果 app.logapp.log.1 的 inode 不同,并且 lsof 显示应用 fd 指向当前 app.log,才说明 reopen 已完成。若旧文件仍带有 (deleted) 或 fd 指向 app.log.1,通常是应用没有处理信号、PID 不对,或 reload 发生在规则执行失败之后。

Linux logrotate 结果示意图:stat 和 lsof 对照 app.log、旧 inode 与应用 fd
图2:用路径、inode 和进程文件描述符交叉确认应用已经切换到新日志文件的结果示意图。

哪些情况不该直接发送 HUP

如果应用没有记录 HUP 的行为,盲目发送可能导致配置重载、连接变化,甚至进程退出。先查应用的日志文档或 service 单元,确认它提供的是 HUP、USR1、systemctl reload myapp,还是只能平滑重启。由 systemd 管理的服务,也可以把通知动作改成明确的 reload 命令,但要确保命令成功返回。

另一个常见边界是权限:logrotate 可能以 root 执行,而应用以 myapp 用户运行。create 的 owner、group 和 mode 要让应用可写,通知脚本还要读取正确的 PID 文件。压缩建议放在应用确认 reopen 之后;对无法 reopen 的程序才考虑 copytruncate,并接受它的短暂数据风险。

常见问题

为什么新 app.log 有内容,应用却还在写 app.log.1?

文件名变化不等于文件描述符变化。检查 HUP/reload 是否被应用支持,再用 lsof 对照 fd 实际指向的 inode。

postrotate 里应该写 kill 还是 systemctl reload?

以应用官方约定为准。PID 稳定且 HUP 明确表示 reopen 时可以用 kill;由 systemd 管理并提供 reload 动作时,systemctl reload 通常更能表达服务边界。

什么时候使用 copytruncate?

只有在应用无法被可靠通知关闭并重新打开日志时考虑。它能保持原 inode,但要评估复制到截断之间的日志丢失窗口和高并发写入影响。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>