Parallel::ForkManagerを訳しました(nekokakさんの0.7.5の翻訳からの差分だけなので楽でした)。
で、複数のエラーログファイルがあって、そのログファイルを tail(File::Tailを使って)しつつ、Errorを見つけたら、何かするっていうスクリプト。
例として、それどうよって感じですね!(ぉ
(一般的には、定期的なデータ更新とかで結構デカ目だけど、複数サーバで分散とかいうほど大げさじゃない(ていうか、予算上そんなサーバないし…)、素直にやると5〜6hくらいかかっちゃうんだけども、どーすっかなーみたいな処理を1hくらいで済ましたいなーみたいな処理に使えば良いと思います -- Web/FTPからデータ(複数ファイル)取ってきて、パースして、DBに突っ込む的なアレ)。
#!/usr/bin/perl use strict; use warnings; use File::Tail; use Proc::PID::File; use Parallel::ForkManager; main(); sub main { my $name = $0; $name =~s{/}{-}g; my $pidfile = Proc::PID::File->new(name => $name, dir => "/tmp/"); # Proc::PID::Fileで複数プロセス動かないようにチェック if (not $pidfile->alive) { $pidfile->touch; tail_error_log($pidfile); } } sub tail_error_log { my $pidfile = shift; my %error; my @files = </var/log/httpd/*error_log>; my $parent_pid = $$; local(@SIG{qw/INT TERM HUP/}); $SIG{TERM} = $SIG{INT} = sub {$pidfile->release; exit(1)}; # cronでたまに動かすようにしてるので、HUPでもexitさせた $SIG{HUP} = sub { $pidfile->release; exit; }; # 子プロセスは終わらないので、ファイルの数だけプロセスが必要 my $pm = Parallel::ForkManager->new(scalar @files); foreach my $file (@files) { my $past_log_ctime = (stat $file . '.1')[10]; # .1 log's epoch time; print "start tail -f $file\n"; my $pid = $pm->start and next; my $tail = File::Tail->new($file) or die "cannot tail log $file"; # -1 渡すと、最初に全体読むそうな $tail->tail(-1); # alarmでlogrotateをチェックする local $SIG{ALRM} = sub { my $past_log_ctime_test = (stat $file . '.1')[10]; if ($past_log_ctime_test != $past_log_ctime) { # ログがローテートされたもよう print "file is changed"; $past_log_ctime = $past_log_ctime_test; # ログファイルがローテートされたので、ファイルを読み直す $tail->resetafter(0); %error = (); } # 親が $pidfile を解放したら(HUP/INT/TERM時)、子プロセスも終了する $pm->finish if not $pidfile->alive; # もっかい、alarmを仕込む alarm(300); }; alarm(300); while (defined(my $error = $tail->read)) { my $error_string = $error; $error_string =~s{^.+?\] Error in}{Error in} or next; $error_string =~s{, referer.+?$}{}; # 初めてのエラー内容のみ処理(ローテートの時にはクリアしてます。ALRMんとこ参照) if (not $error{$error_string}++) { do_something($error); } } $pm->finish; } $pm->wait_all_children; } sub do_something { # 何かする }