新しいblogに移行しました

新ブログ "All Yout Bugs Are Belong To Ass" に移行しました!

2011-01-27

[Perl]evalの中で起こったエラーを検出する

この前、実稼働中のかなり古いコードの不具合対応ということで、そのコードをチェックしていたのですが、その中で
my $dbh = DBI->connect(...);
eval {  
    my $sth = $dbh->prepare(...);
    $sth->execute;
    $sth->finish;
};
みたいな箇所(上のコードは実物じゃないですよ)があったんですね。で、evalで被ってやるのはまあ100歩譲って「仕方ないなあ」で済ませましたけど、よりによって、$@に入ってきた値をキャッチするロジックがなかったんです。
当然エラーとかが起こってもエラーログすら吐かず、何食わぬ顔で突っ走ってしまう。そんなわけで、SQL周りの不具合を見つけるのに数時間を要してしまったわけです。

その場ですぐに修正パッチを作成・適用して事なきを得ましたが、僕は心の奥底で思いました。「evalの中でエラーが起こったら、無理やりにでもエラーログにその内容を吐かせたい!」と。

で、調べてみたら、$SIG{__DIE__}にコードリファレンスを食わせることで、その願いは叶えられるとのことでした。
これを使って、ついでにエラー発生時刻も併せて吐いてやれば、後の不具合調査時に役立つでしょう。

というわけで、試しにコードを書いてみました。
use warnings;
use strict;
use Time::Zone;
use DateTime;
use DBI;

$SIG{__DIE__} = sub {
    my $now = DateTime->from_epoch( epoch => time() + tz_local_offset() )->strftime( '%Y/%m/%d %H:%M:%S' );
    warn join( " - ", $now, "CRITICAL", shift );
};

### DBD::sqrightなんてないので、このコードはエラーとなる。
eval {
    my $dbh = DBI->connect( 'dbi:sqright:hoge.txt' );
};

END {
    print "FINISHED!!!\n";
}

この動作結果は、以下の様になります。
2011/01/27 15:19:20 - CRITICAL - Can't locate DBD/sqright.pm in @INC (@INC contains: /home/yt/perl5/perlbrew/perls/perl-5.12.2/lib/site_perl/5.12.2/x86_64-linux /home/yt/perl5/perlbrew/perls/perl-5.12.2/lib/site_perl/5.12.2 /home/yt/perl5/perlbrew/perls/perl-5.12.2/lib/5.12.2/x86_64-linux /home/yt/perl5/perlbrew/perls/perl-5.12.2/lib/5.12.2 .) at (eval 901) line 3.
2011/01/27 15:19:20 - CRITICAL - install_driver(sqright) failed: Can't locate DBD/sqright.pm in @INC (@INC contains: /home/yt/perl5/perlbrew/perls/perl-5.12.2/lib/site_perl/5.12.2/x86_64-linux /home/yt/perl5/perlbrew/perls/perl-5.12.2/lib/site_perl/5.12.2 /home/yt/perl5/perlbrew/perls/perl-5.12.2/lib/5.12.2/x86_64-linux /home/yt/perl5/perlbrew/perls/perl-5.12.2/lib/5.12.2 .) at (eval 901) line 3.
Perhaps the DBD::sqright perl module hasn't been fully installed,
or perhaps the capitalisation of 'sqright' isn't right.
Available drivers: DBM, ExampleP, File, Gofer, Proxy, SQLite, Sponge.
 at /home/yt/perl/warn.pl line 13

ちなみにこちらの記事によれば、これだけでは処理が不十分のようで、$^Sを使ってeval経由の呼び出しか否か(=本当にdieする時)で処理を分けた方が良い様です。

0 件のコメント: