下面是我尝试使用 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 很混乱,因此最好尽可能避免。