如何使用 Log4perl 在多线程 perl 应用程序中轮换日志文件



下面是我尝试使用 log4perl 在多线程应用程序中旋转日志文件的示例代码。但是除非它是多线程应用程序,否则它可以正常工作。日志不会轮换,日志文件的大小会增加。谁能指导我哪里出错了?

use strict;
use warnings;
use Log::Log4perl;
use POSIX;
use threads;
use threads::shared;
my @InputFiles;
my $InputDirectory=$ARGV[0];
my $LogName=$ARGV[1];
opendir(DIR,$InputDirectory) or die "could not open the input directory";
@InputFiles=readdir(DIR);
close(DIR);
my $file;
    #logger_configuration
my $log_conf ="
   log4perl.rootLogger              = DEBUG, LOG1
   log4perl.appender.LOG1           = Log::Dispatch::FileRotate
   log4perl.appender.LOG1.filename  = $LogName
   log4perl.appender.LOG1.mode      = append
   log4perl.appender.LOG1.autoflush = 1
   log4perl.appender.LOG1.size      = 10000
   log4perl.appender.LOG1.max       = 20
   log4perl.appender.LOG1.layout    = Log::Log4perl::Layout::PatternLayout
   log4perl.appender.LOG1.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}|%P|%m|%n
";
#loading the configuration file
Log::Log4perl::init($log_conf);
#creating logger instance
my $logger = Log::Log4perl->get_logger();
my $thread_count=5;
my $file_total= scalar @InputFiles;
#print STDERR "$file_totaln";
#dividing total files among the no of given threads
my $div = $file_total/$thread_count;
$div = ceil($div);
my $start = 0;
my $end = $div;
my @threads;
for (my $count = 1; $count <=$thread_count ; $count++) 
{
    my $thread = threads->new(&process,$start,$end);
    push(@threads,$thread);        
    $start = $end;
    $end = $end + $div;
    if ($end > $file_total)
    {
        $end = $file_total;
    }
}
foreach (@threads) 
{
   $_->join;
}
sub process
{
    my $lstart = shift;
    my $lend = shift;
    my $id = threads->tid();
    for (my $index = $lstart; $index < $lend; ++$index) 
    {   
      $logger->info($InputFiles[$index]);
    }
}

好的,从根本上说,你的问题是这样的 - 你的"记录器"是在线程开始之前创建的。这意味着您的所有线程都将具有相同的文件句柄。

这本身会给您带来问题,除非您有某种针对文件 IO 的仲裁机制。将您的线程视为单独的程序,所有程序都试图打开并写入同一个文件 - 您可以看到它可能会变得多么混乱。

相反,我建议您需要做的是为记录器创建另一个线程,并通过类似 Thread::Queue

use Thread::Queue;
my $log_q = Thread::Queue -> new(); 
sub logger_thread {
     #init logger here 
    while ( my $log_item = $log_q -> dequeue() ) {
        $logger -> info ( $log_item );
    }
}
my $log_thread = threads -> create ( &logger_thread ); 

然后将$logger -> info (....)替换为:

$log_q -> enqueue($message_to_log);

然后,一旦你加入了所有的"进程"线程(例如,就像你现在一样(,关闭记录器线程:

$log_q -> end();
$log_thread -> join(); 

这将导致每个线程对日志消息进行排队,一旦它们完成(并加入(,您就会关闭队列,以便记录器知道它已经"完成" - 因此一旦队列为空并且可以加入,就会退出。

多线程文件 IO 很混乱,因此最好尽可能避免。

最新更新