Logrotate 不截断打开的文件

Logrotate 不截断打开的文件

我有一个同时充当测试和演示平台的系统。我正在记录系统输出的大量数据,而在部署时我不会记录这些数据,因为我的日志文件很快就会填满,最终耗尽了硬盘上的分区空间。

我尝试过logrotate,但似乎只有在我的程序未运行时才有效。当程序关闭时,它会正确截断文件,记录压缩版本,并丢弃其余部分。但是,当填充日志文件的程序启动并运行时,它将正确创建压缩文件,但不会截断活动文件。我正在尝试找出如何将其截断。

我正在通过简单的重定向写入日志文件。 ProgramA>logA等等programB>logB。我的程序每次收到输入向量时都会输出,因此输出速度非常快。我的假设是 logrotate 由于不断写入字段而无法截断,但任何人都可以确认这就是原因吗?

logrotate 到底在哪里保存它的错误文件?

另外:我想修改 logrotate 的行为。如果它激活并看到一个大的日志文件,比如 100 MB,我希望它创建一个副本仅有的在截断原始日志文件之前,先删除文件的最后 1 MB(丢弃文件中较旧的 99 MB 内容)。我需要保留最新的数据,但我不太关心任何很旧的数据。谁能告诉我该怎么做?

答案1

我知道这有点旧,但我遇到了同样的问题,解决方案是使用附加重定向到输出文件而不是破坏它。这样您的应用程序/脚本就不会独占锁定文件。

代替:

ProgramA > logA

使用:

ProgramA >> logA 

问候。

答案2

man logrotate

   copytruncate
          Truncate the original log file to zero size in place after creat‐
          ing a copy, instead of moving the old  log  file  and  optionally
          creating  a  new one.  It can be used when some program cannot be
          told to  close  its  logfile  and  thus  might  continue  writing
          (appending) to the previous log file forever.  Note that there is
          a very small time slice between copying the file  and  truncating
          it,  so  some  logging  data  might be lost.  When this option is
          used, the create option will have no effect, as the old log  file
          stays in place.

这可能是您需要放入配置文件中的选项。

关于您的其他问题:使用postrotate指令运行命令以在旋转文件后截断文件。在这种情况下,您应该在截断之后在此阶段手动压缩它。(实际上不是,最后一部分,因为logrotate不压缩第一个存档。)

编辑:关于保留最后 1 MB 的更多信息。您可以将其配置为在日志文件超过 200 K 时进行轮换,并且仅保留 5 个备份。这样您将始终保留大约最后 1 MB。如果第一个备份太大,你可以等到5个备份完成后它“脱落”。

答案3

正如所承诺的,一个“足够接近”的环形缓冲区程序,用几行 Perl 语言编写。

要使用它,请将所需日志文件的大小(以字节为单位)以及两个或更多日志文件传递给它。它会每隔 X 字节在日志文件之间切换。我确信已经有一个程序可以做到这一点,但我无法通过快速搜索找到它。例如,

$ your-app | log-splitter 1048576 /var/log/your-app/log1 /var/log/your-app/log2

当然,假设该文件log-splitter在 $PATH 中是可执行的,并且包含:

#!/usr/bin/perl
use warnings qw(all);
use strict;
use autodie;
use Carp;
use Fcntl qw(SEEK_SET);
use IO::Handle;

# yes, we truncate all logs at start. Sorry.
@ARGV >= 2 or croak "Usage: $0 log-size log-file...";
my ($max_size, @lognames) = @ARGV;
my @logs = map {
    open my $fh, '+>', $_;
    $fh->autoflush(1);
    $fh;
} @lognames;

my $current_size = $max_size;
my $current_log  = $#logs;
while (defined(my $line = <STDIN>)) {
    if ($current_size >= $max_size) {
        $current_log  = ($current_log + 1) % @logs;
        $current_size = 0;
        seek($logs[$current_log], 0, SEEK_SET);
        truncate($logs[$current_log], 0);
    }

    $current_size += length($line);
    print {$logs[$current_log]} $line;
}

close($_) foreach @logs;

要澄清有关该代码的任何许可问题:

CC0
在法律允许的范围内,Anthony DeRobertis 放弃了上述程序(log-splitter)的所有版权和相关或邻接权。本作品出版地:美国。

相关内容