Perlサンプル18 エラーを表示するCGI
投稿:2020-02-07
CGIの実行でエラーが発生すると「500 Internal Server Error」と表示されるだけで、エラーの内容がさっぱり分かりません。 エラーが発生した際に、どんなエラーがどの行で起きたのか分かれば対策も素早くできますよね。
#/usr/bin/perl
use utf8;
use strict;
use warnings;
use CGI qw/ -utf8 :html /;
use DateTime;
use Encode::Locale;
binmode STDOUT, ":utf8";
use constant TITLE => "Perlサンプル18の実行結果";
# エラー発生時のトレースバックを記録
my @trace_info;
$SIG{__DIE__} = sub {
for (0 .. 2) {
my (undef, $filename, $line, $subroutine) = caller $_;
push @trace_info, "$filename - line $line. → $subroutine<br />" if $filename;
}
};
my $cgi = CGI->new;
print $cgi->header(
-charset => "utf-8",
);
print $cgi->start_html(
-title => TITLE,
-lang => "ja",
);
print $cgi->p(TITLE);
my $dt = DateTime->now(time_zone => "local");
print $cgi->p($dt->strftime("只今の日時は%F %Tです。"));
my $runcondition = $cgi->param("runcondition");
print $cgi->p("処理内容:$runcondition");
print $cgi->p("処理開始");
# 実際にやりたい処理
my @out_html;
eval {
# evalの中でエラーが発生するとevalの外に抜ける
@out_html = myfunc($runcondition);
};
# エラー発生の場合
if ($@) {
# エラーメッセージを表示
print $cgi->p("エラー発生");
print $cgi->p($@);
# トレースバックを表示
print $cgi->p("トレースバック");
print $cgi->p($_) for @trace_info;
} else {
# 正常に処理した場合
print $_ for @out_html;
}
print $cgi->p("処理終了");
print $cgi->a({ href => "../sample/0i_perlsample_018.html" }, "元のページに戻る");
print $cgi->end_html();
exit;
# やりたい処理(本当?)
sub myfunc {
my ($run) = @_;
my @html;
if ("perlerror" eq $run) {
# Perlのエラー発生
# 未定義のサブルーチンを呼んでエラーを発生させる
myfunc_undef();
} elsif ("die" eq $run) {
# 自分でエラーを発生させる
die "何かエラーが見つかりました(泣";
} elsif ("normal" eq $run) {
# 正常ケース
push @html, $cgi->p("正常に処理しました♪");
} else {
# フォーム不一致
die "予定外のフォームから送信されました(あれ?)";
}
return @html;
}
実行できるフォームを用意しました。 エラーの種類を選択して送信ボタンをクリックしてください。
正常ケースの場合です。 エラーを検知していないので特に表示されません。
Perlサンプル18の実行結果 只今の日時は2020-02-07 17:51:27です。 処理内容:normal 処理開始 正常に処理しました♪ 処理終了 元のページに戻る
Perlのエラーが発生した場合です。 このケースは発生場所が76行目と分かります。
Perlサンプル18の実行結果 只今の日時は2020-02-07 17:53:11です。 処理内容:perlerror 処理開始 エラー発生 Undefined subroutine &main::myfunc_undef called at perlsample_018.cgi line 76. トレースバック perlsample_018.cgi - line 76. → main::__ANON__ perlsample_018.cgi - line 43. → main::myfunc perlsample_018.cgi - line 41. → (eval) 処理終了 元のページに戻る
処理中にエラーを見つけてdieした場合です。 このケースは発生場所が79行目と分かります。
Perlサンプル18の実行結果 只今の日時は2020-02-07 17:58:05です。 処理内容:die 処理開始 エラー発生 何かエラーが見つかりました(泣 at perlsample_018.cgi line 79. トレースバック perlsample_018.cgi - line 79. → main::__ANON__ perlsample_018.cgi - line 43. → main::myfunc perlsample_018.cgi - line 41. → (eval) 処理終了 元のページに戻る
処理したい内容をevalで囲むと処理中のエラーを捕捉してevalを抜けるのでPerlのエラーで止まるのを防止できます。 エラーの有無は$@にエラーメッセージがあるかどうかで判断します。 エラーの発生場所が共通サブルーチンですと、その呼び出し元を知りたくなるのでトレースバックを取得します。 エラー発生のタイミングで$SIG{__DIE__}に設定したサブルーチン(のリファレンス)が呼ばれるので、その際に記録します。 トレースバック情報はcallerを呼んで取得します。