如何在logrotate移动stdout文件后继续将其重定向到另一个文件?

我有一个简单的脚本,将一堆日志输出到屏幕,并将STDOUT重定向到文件中以存储这些日志。由于该脚本长时间运行,所以我需要对日志文件进行轮转,使其分成更小、更易管理的文件。 我遇到的问题是,一旦"logrotate"将当前日志文件移到新文件中,新创建的日志文件就不再被填充日志。似乎原始日志文件被删除后,其文件处理程序丢失了,重定向也无法再起作用。 我还发现这篇帖子中有与我相同的问题,并声称可以通过使用“>>”而不是“>”来重定向输出来解决。我尝试了他的解决方案,但对我没有起作用。是否有人知道如何保持重定向正常工作的方法?

4在你的情况下,我仍然建议使用 ">>" 而不是 ">" 来写入截断文件:因为 ">>" 以追加模式打开,每次写入时都会寻找文件的末尾。这样,当你截断文件(将其从XXXX字节变为0字节)时,它会 "寻找到末尾",所以它会知道现在必须在字节0之后写入。否则,它可能会在字节XXXX之后写入,并在此之前创建一个稀疏文件,其中包含XXXX个空字节(即,当使用 ">" 时,文件描述符只需记住它在文件中的位置,并从那里开始写入,而不会意识到文件大小缩小了!) - Olivier Dulac
4个回答

你应该在你的logrotate配置中使用copytruncate指令来处理这个日志文件。 copytruncate指令会在创建副本后直接对原始日志文件进行截断,而不是移动旧的日志文件并可选择创建一个新文件。当某些程序无法关闭其日志文件并可能继续写入(追加)到之前的日志文件时,可以使用该指令。请注意,在复制文件和截断文件之间存在一个非常小的时间片,因此可能会丢失一些日志数据。当使用此选项时,create选项将不起作用,因为旧的日志文件保持原位。

2可能值得提及的是:在compress操作之前,数据会被复制一次。这曾经给我们造成了问题,不过那是我们的错误,因为我们不应该离lv空间限制那么近。另外,正如man代码片段中所述,在复制和截断操作之间可能会丢失一些日志数据。 - Belmin Fernandez

作为一个替代方案,您还可以: 使用脚本中的日志记录工具而不是管道,使用专用设施(例如local5),例如: logger -p local5.info -t myscriptname "这是一些日志数据" 配置syslog将此设施写入所需的日志文件,例如(rsyslog.conf): local5.* /var/log/mylogfile 为此日志设置logrotate规则。

只有在你有像echo这样的显式输出命令时,这才有效。从脚本中调用并输出内容的第三方工具的输出不能以这种方式重定向到日志记录器。 - Daniel Alder

另一种替代Iain解决方案的方法是使用一个postrotate脚本,在旋转完成后重新启动您的脚本。这对于许多守护进程(重新启动或重新加载守护进程)都是这样做的,但由于不了解您的脚本,我不知道这个解决方案是否适合您(您的脚本是否依赖于一段时间前生成的某些状态)。

/etc/logrotate.d/your-script-name的内容:

/var/log/your-script-name.log {
    # your current logrotate options
    ...
    postrotate
        # this supposing you have the current pid stored
        cat /run/your-script-name.pid | xargs -r kill
        #relaunch it again
        /usr/local/bin/your-script-name
    endscript
}

你可以将stdout导入到"split"(Linux的核心工具之一)中。它允许你根据大小、行数等将文件/标准输入分割成块。一旦你将其分割成块,如果需要,你可以使用logrotate来管理它。