在Perl中跨文件轮换监控文件内容

4

下午好,

我有一个小型的Perl脚本,主要是模拟Minecraft服务器日志上的tail -f功能。该脚本检查特定的字符串,并以各种方式进行操作。

以下是脚本的简化版本:

#!/usr/bin/perl

use 5.010;
use warnings;
use strict;

my $log = "PATH TO LOG";
my $curpos;

open(my $LOGFILE, $log) or die "Cannot open log file";

# SEEK TO EOF
seek($LOGFILE, 0, 2);

for (;;){
        my $line = undef;

        seek($LOGFILE,0,1);  ### clear OF condition
        for($curpos = tell($LOGFILE); <$LOGFILE>; $curpos = tell($LOGFILE)){
                $line = "$_ \n";


                if($line =~ /test string/i){
                        say "Found test string!";
                }
        }

        sleep 1;
        seek($LOGFILE,$curpos,0); ### Setting cursor at the EOF
}

当我有一个测试服务器时,一切似乎都正常。但在生产环境中,server.log文件会被轮换。当日志轮换时,脚本仍然持有原始文件,而不是替换它的文件。 例如,监视server.log文件,server.log被移动并压缩到logs/date_x.log.gz,server.log现在变成了一个新文件。
我该如何调整我的脚本以监视文件名“server.log”,而不是当前称为“server.log”的文件?
2个回答

6
你有没有考虑只使用tail -F作为你的脚本输入呢?
tail -F server.log 2>/dev/null | perl -nE 'say if /match/'

这将监视指定的文件,将每一行传递到您的脚本的标准输入上。它将正确地跟踪只有server.log,如下所示:

echo 'match' >server.log

(由脚本匹配)

mv server.log server.log.old
echo 'match' >server.log

(也匹配)

您可以使用下面的Perl代码打开tail -F文件:

open(my $fh, '-|', 'tail -F server.log 2>/dev/null') or die "$!\n";

太好了,我之前不知道-F标志,它非常有用!脚本已经重新制作过了,现在看起来更加清晰! - forquare

3
您可以使用 stat() 函数来检查文件名和文件句柄的 inode号。如果它们不同,那么日志文件已被轮换,需要重新打开文件以进行读取。 $readline 是一个迭代器(通过 get_readline($file_name) 获得),它会自动处理这些变化,并做出正确的操作。
use strict;
use warnings;

sub get_readline {
  my ($fname) = @_;
  my $fh;

  return sub {
    my ($i1, $i2) = map { $_ ? (stat $_)[1] : 0 } $fh, $fname;

    if ($i1 != $i2) {
      undef $fh;
      open $fh, "<", $fname or return;
    }
    # reset handle to current position
    seek($fh, 0, 1) or die $!;
    return wantarray ?  <$fh> : scalar <$fh>;
  };
}

`seq 11 > log_file`;
my $readline = get_readline("log_file");

print "[regular reading]\n";
print $readline->();
print "[any new content?]\n";
print $readline->();

`rm log_file; seq 11 > log_file`;
print "[reading after log rotate]\n";
print $readline->();

输出

[regular reading]
1
2
3
4
5
6
7
8
9
10
11
[any new content?]
[reading after log rotate]
1
2
3
4
5
6
7
8
9
10
11

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接