仅在文件中保留最近的 n 行输出

仅在文件中保留最近的 n 行输出

我正在计算集群上运行一个程序,该程序由于分段错误而定期崩溃,因此我想保留详细的调试输出来诊断原因。但是,该程序可能会运行多个小时而不会崩溃,因此,如果我使用 将整个输出重定向到一个文件srun my-program-binary > output.log 2>&1,结果是一个日志文件太大,以至于超出了集群上的磁盘配额。由于我只需要最近的 n 行输出(例如,大约 1000 行),因此我想知道是否有一种方法可以保留输出的运行缓冲区,并仅保留给定时间的最新部分以供将来参考。

我可以在集群上安装的内容受到限制,因此我需要一个不需要非标准软件包的解决方案。这个解决方案不适合我的情况,因为我不能等到文件创建后才保留最后一部分,我需要保留一个永远不会超过特定长度/内存大小的运行缓冲区。有谁知道有办法做这样的事情吗?

答案1

如果你有bash,wcsed,你可以使用一个小脚本:

#!/bin/bash

file=$1
nlines=$2

while read line ; do
    echo "$line" >> $file
    read clines _ <<< $(wc -l $file)
    if [[ clines -gt nlines ]] ; then
        n=$(( clines - nlines ))
        sed -i -e 1,${n}d $file
    fi
done

与管道一起使用:srun my-program-binary 2>&1 | a_little_script output.log 1000

该脚本确实

  • 从输出中读取每一行my-program-binary并将其回显到output.log
  • 获取output.log中的当前行数
  • 删除前n行超出nlines

这样,您的日志文件中始终会保留最后 n 行输出。它使用很少的内存和CPU。

答案2

如果内存可以容纳 1000(或左右)行,一种方法是在内存中缓冲这些行,直到发生某些情况:

#!/usr/bin/env perl
# keepn - keep N lines of input in memory, and print them at exit
$0 = 'keepn';    # pkill(1) target (some OS do not honor this)
my $keep = shift // 1000;
my $okay = 1;
$SIG{$_} = sub { $okay = 0 } for qw(HUP INT PIPE TERM USR1);

my @buf;
while ( $okay and defined( my $line = readline ) ) {
    push @buf, $line;
    my $over = @buf - $keep;
    if ( $over > 0 ) { splice @buf, 0, $over }
}
END { print for @buf }

这里的缺点是,如果keepn进程被终止,那么缓冲的日志行也会被终止。信号处理可能有助于防止其中一些边缘情况。定期同步到@buf磁盘会增加 I/O,但更好地确保有一些日志行,但希望keepn不是不可靠或成为失控的内存不足杀手的目标。

srun my-program-binary 2>&1 | keepn 1000 > output

将日志发送到其他地方

另一种选择是通过管道传输logger(1)并让 syslog 守护进程处理输出需要到达的位置,但这需要控制 syslog 守护进程或日志记录框架。这可能意味着日志可能会累积在其他地方,您可能需要消耗更大的磁盘配额。但设置起来会复杂得多。 TCP 套接字或 Web API 也可用于将日志输出发送到其他地方,无论好坏。

多路复用器

终端多路复用器例如tmux提供有限的回滚;我们可以简单地在下面运行作业tmux,然后使用缓冲区回滚来查看日志的最后几行是什么,直到设置 history-limit

set-option -g history-limit 1000

相关内容