我正在计算集群上运行一个程序,该程序由于分段错误而定期崩溃,因此我想保留详细的调试输出来诊断原因。但是,该程序可能会运行多个小时而不会崩溃,因此,如果我使用 将整个输出重定向到一个文件srun my-program-binary > output.log 2>&1
,结果是一个日志文件太大,以至于超出了集群上的磁盘配额。由于我只需要最近的 n 行输出(例如,大约 1000 行),因此我想知道是否有一种方法可以保留输出的运行缓冲区,并仅保留给定时间的最新部分以供将来参考。
我可以在集群上安装的内容受到限制,因此我需要一个不需要非标准软件包的解决方案。这个解决方案不适合我的情况,因为我不能等到文件创建后才保留最后一部分,我需要保留一个永远不会超过特定长度/内存大小的运行缓冲区。有谁知道有办法做这样的事情吗?
答案1
如果你有bash
,wc
和sed
,你可以使用一个小脚本:
#!/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