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する時)で処理を分けた方が良い様です。