3

下面是我尝试使用 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_total\n";

#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]);
    }
}
4

1 回答 1

3

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

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

相反,我建议您需要为记录器创建另一个线程,并通过类似的方式发送 IOThread::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比较乱,所以尽量避免比较好。

于 2014-12-02T16:02:54.467 回答