【问题标题】:How to tail file in perl when copytruncate is used使用copytruncate时如何在perl中尾随文件
【发布时间】:2016-08-04 03:02:32
【问题描述】:

问题

我创建了一个简单的 perl 脚本来读取日志文件并异步处理数据。
读取子还检查 inode 编号的变化,以便在日志轮换时创建一个新的文件句柄。

我面临的问题是,当 copytruncate 用于 logrotation 时,当文件旋转时 inode 不会改变。
这应该不是问题,因为脚本应该只是继续读取文件,但由于某种我无法立即看到的原因,一旦日志轮换,就不会读取任何新行。


问题

如何修改以下脚本(或完全废弃并重新开始)以使用 perl 持续跟踪使用 copytruncate 进行日志旋转的文件?


代码

use strict;
use warnings;

use threads;
use Thread::Queue;
use threads::shared;

my $logq = Thread::Queue->new();
my %Servers :shared;
my %servername :shared;

#########
#This sub just reads the data off the queue and processes it, i have
#reduced it to a simple print statement for simplicity.
#The sleep is to prevent it from eating cpu.
########

sub process_data
{
        while(sleep(5)){
                if ($logq->pending())
                {
                        while($logq->pending() > 0){
                                my $data = $logq->dequeue();
                                print "Data:$data\n";
                        }
                }
        }
}

sub read_file
{
        my $myFile=$_[0];
        #Get the argument and assign to var.

        open(my $logfile,'<',$myFile) || die "error";
        #open file

        my $Inode=(stat($logfile))[1];
        #Get the current inode

        seek $logfile, 0, 2;
        #Go to the end of the file

        for (;;) {
                while (<$logfile>) {
                        chomp( $_ );
                        $logq->enqueue( $_ );
                        #Add lines to queue for processing

                }
                sleep 5;
                if($Inode != (stat($myFile))[1]){
                        close($logfile);
                        while (! -e $myFile){
                                sleep 2;
                        }
                        open($logfile,'<',$myFile) || die "error";
                        $Inode=(stat($logfile))[1];
                }
                #Above checks if the inode has changed and the file exists still

                seek $logfile, 0, 1;
                #Remove eof

        }

}


my $thr1 = threads->create(\&read_file,"test");
my $thr4 = threads->create(\&process_data);
$thr1->join();
$thr4->join();
#Creating the threads, can add more log files for processing or multiple processing sections.

可能相关的信息

logrotate 的日志配置包含

compress
compresscmd /usr/bin/bzip2
uncompresscmd /usr/bin/bunzip2
daily
rotate 5
notifempty
missingok
copytruncate

对于这个文件。

规格

GNU bash, version 3.2.57(1)-release (s390x-ibm-linux-gnu)
perl, v5.10.0
(if logrotate has version and someone knows how to check then i will also add that)

如果需要更多信息,请询问。

【问题讨论】:

  • 我几乎解决了这个问题,当文件被截断时,文件中的指针停留在末尾,所以我只需要检查一下。我会尽快添加答案,或者如果其他人这样做了,我会接受。

标签: linux bash perl logrotate


【解决方案1】:

因此,当您查看 copytruncate 时,失败的原因非常明显,它会复制原始文件,然后截断当前文件。
虽然这确保了 inode 被保留,但它产生了另一个问题。

由于当前我拖尾文件的方式是简单地停留在末尾并删除 eof 标志,这意味着当文件被截断时,指针停留在截断前最后一行的位置,这反过来意味着在再次到达该指针之前不会再读取任何行。

那么显而易见的解决方案是简单地检查文件的大小并在指针指向文件末尾时重置指针。

我发现使用下面的两行来检查文件大小从未变小更容易。

my $fileSize=(stat($logfile))[7];
#Added after the inode is assigned

变化

if($Inode != (stat($myFile))[1]){

if($Inode != (stat($myFile))[1] || (stat($myFile))[7] < $fileSize){

【讨论】:

  • 您可能还想查看File::Tail,它使用sysseekSEEK_END 来检查文件结束位置。
猜你喜欢
  • 1970-01-01
  • 2012-09-24
  • 1970-01-01
  • 1970-01-01
  • 2012-05-20
  • 1970-01-01
  • 2022-11-21
  • 2011-08-28
  • 1970-01-01
相关资源
最近更新 更多