使用 copytruncate 时如何在 perl 中跟踪文件
How to tail file in perl when copytruncate is used
问题
我创建了一个简单的 perl 脚本来异步读取日志文件和处理数据。
读取子程序还会检查 inode 编号的变化,以便在日志轮换时创建新的文件句柄。
我面临的问题是,当 copytruncate
用于 logrotation 时,inode 在文件旋转时不会改变。
这应该不是问题,因为脚本应该继续读取文件,但由于某些我无法立即看到的原因,一旦日志轮换,就不会读取新行。
问题
我如何修改以下脚本(或完全废弃并重新开始)以使用 perl 连续跟踪使用 copytruncate
logrotated 的文件?
代码
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)
如需更多信息,请询问。
因此,当您查看 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){
问题
我创建了一个简单的 perl 脚本来异步读取日志文件和处理数据。
读取子程序还会检查 inode 编号的变化,以便在日志轮换时创建新的文件句柄。
我面临的问题是,当 copytruncate
用于 logrotation 时,inode 在文件旋转时不会改变。
这应该不是问题,因为脚本应该继续读取文件,但由于某些我无法立即看到的原因,一旦日志轮换,就不会读取新行。
问题
我如何修改以下脚本(或完全废弃并重新开始)以使用 perl 连续跟踪使用 copytruncate
logrotated 的文件?
代码
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)
如需更多信息,请询问。
因此,当您查看 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){