在日志管理领域,logrotate 无疑是 Linux 系统管理员最熟悉的工具之一。它通常会随操作系统预装,开箱即可使用,但默认配置仅相当于一个通用模板。若想让其精确服务于特定应用,核心做法是直接编辑 /etc/logrotate.d/ 目录下的专用配置文件,而非修改全局主配置 /etc/logrotate.conf。

如何判断 logrotate 是否正常运行
首先需要明确:logrotate 本身并非一个常驻后台的守护进程,而是依赖系统的 cron 定时任务来触发执行。因此,判断 logrotate 是否正常运作,实质上是检查其触发机制是否完好。
有几个命令可以快速验证:
ls -l /etc/cron.daily/logrotate:若该文件存在,通常说明系统已配置每日自动执行任务。grep logrotate /var/lib/logrotate/status:该命令可查看状态文件中记录的各日志最近一次轮转时间戳,是很有价值的参考。systemctl list-timers | grep logrotate:在较新的 systemd 系统上,可用此命令确认是否有对应的定时器处于活跃运行状态。
如果以上检查均无反馈,不必急于重装 logrotate。问题多半出在 cron 服务本身。可使用 systemctl status cron 或 systemctl status crond 确认 cron 服务是否处于正常运行状态,同时检查 /etc/cron.daily/ 目录下的 logrotate 脚本是否被意外注释或修改。
为何 daily 配置未能如期执行日志切割
这是许多新手常遇到的困惑。配置了 daily 参数后,期待次日日志自动切割,却发现毫无变化。问题的根源在于 logrotate 的工作机制。
其时间判断依赖一个状态文件:/var/lib/logrotate/status。该文件仅在执行完实际轮转操作后才会更新,从而引发以下典型情况:
- 首次配置未能立即生效:例如,你在下午配置了
daily选项,它不会立即执行切割。需等到次日 cron 任务运行时,才会根据状态文件判断是否已满 24 小时。由于首次无历史记录,其行为可能不确定。 - 空日志文件:若启用了
notifempty选项(许多配置默认启用),即使时间到达,日志文件为空时,logrotate 也会跳过轮转。 - 路径陷阱:
missingok选项虽允许日志文件不存在时不报错,但这也可能掩盖路径错误。例如,将/var/log/nginx/access.log误拼为/var/log/nginx/acess.log,由于missingok存在,logrotate 会静默跳过,导致你以为配置正确。 - 权限与信号:logrotate 通常以 root 身份运行,但在
postrotate脚本中若使用类似kill -USR1 `cat /var/run/nginx.pid`的命令通知服务重新打开日志,需确保两点:一是 pid 文件可读,二是 nginx 主进程由 root 启动(或当前用户有权限向该进程发送信号),否则信号发送将失败。
如何安全测试新配置,避免影响线上服务
在线上环境修改日志配置时,最担心因操作失误导致服务异常。因此,务必遵循安全的测试流程,而非被动等待 cron 触发。
第一步,永远先用调试模式模拟一遍:
logrotate -d -f /etc/logrotate.d/myapp
其中 -d(调试)参数至关重要。它会完整模拟轮转过程,包括读取配置、检查条件以及计划执行的操作,但不会实际重命名或压缩任何文件,也不会运行 postrotate 脚本。仅会在终端输出其“打算”执行的操作。
你需要重点关注输出中的以下几行:
rotating pattern:确认它是否正确识别了你配置的日志文件路径。switching to configuration file:确认它加载的是你刚修改的配置文件。- 任何以
error:开头的行:这会直接暴露语法错误或路径权限等致命问题。
模拟执行无误后,第二步才是强制执行一次轮转:
logrotate -f /etc/logrotate.d/myapp
执行后,立刻进行现场检查:
- 查看原日志文件是否按预期变成了空文件(如果用了
copytruncate)或被重命名(如果用了create)。 - 检查新生成的日志文件权限、属主是否与配置中
create 0644 user group设置一致。 - 最关键的一步:用
tail -f 原日志路径命令持续跟踪,观察你的应用服务是否仍在正常地向该文件写入新日志内容。若写入停止,说明服务可能因文件句柄问题“卡住”。
copytruncate 与 create 的关键选择要点
这是 logrotate 配置中最核心的两个选项,它们从不同角度应对日志丢失的风险,一旦选错场景可能带来严重后果。
copytruncate:
这是为“不听话”的服务准备的保底方案。某些应用(例如部分 Java 程序、自定义守护进程)无法通过信号通知其重新打开日志文件。此时可以使用 copytruncate。它的逻辑是先复制当前日志内容,然后清空原文件。服务可继续往被清空的原文件描述符中写入,无需重启。
代价:在“复制”与“清空”两个操作之间存在极短的窗口期。若恰巧在这几毫秒内有大量日志写入,有可能丢失这部分数据。因此,它以“可能丢失微量日志”的风险,换来了服务的绝对连续性。
create:
这是标准、优雅的做法。logrotate 将当前日志文件重命名(例如从 app.log 变成 app.log.1),然后创建一个全新的同名的空文件(app.log)。
前提:你的服务必须能响应 postrotate 脚本中发送的信号(例如给 Nginx 发 USR1,给 Apache 发 USR1 或使用 graceful),使其关闭旧文件句柄,重新打开新创建的文件。如果服务不响应信号,它会继续往已被重命名的旧文件(现在是 app.log.1)中写入日志,导致你看不到最新日志,仿佛日志“消失”了。
还有一个容易出错的细节是 sharedscripts 指令。如果你的配置匹配了多个日志文件(例如使用了通配符 /var/log/myapp/*.log),并且配置了 postrotate 脚本,那么必须加上 sharedscripts。否则,logrotate 会为每一个匹配到的日志文件单独执行一遍 postrotate 脚本。想象一下,你本意是让 Nginx 重启一次日志句柄,结果因为匹配了 access.log 和 error.log,kill -USR1 命令被执行了两次,这很可能导致错误。
总之,无论采用哪种方式,最根本的是必须充分了解你的应用程序如何处理日志文件。某些程序会缓存文件描述符,或忽略文件 inode 的变化。对于这类程序,create 模式可能失效,copytruncate 成为唯一可行选项,但需接受其带来的微小数据丢失风险。深入理解服务的日志写入机制,才是成功配置的关键。
