Apache2::Reload 使いたい,コンパイルエラーもトラップしたい

開発中は Apache2::Reload 使うと,変更されたモジュールを検出してリロードしてくれて便利なんですが,

  • Class::Data::Inheritable と相性が悪い
    • Apache2::Reload が reload した場合,mk_classdata() が呼び出されないので,クラスデータを使うとエラーが起きる(Not a CODE reference とか怒られる)
  • スクリプトの記述にエラーがあった場合,Internal Server Error になってしまう
    • 初回に読み込まれるときはちゃんと Response ハンドラでトラップできるんですが,Apache2::Reload が reload した時はトラップできませんされません

などの弊害があります。前者については Class::Data::Reloadable なるモジュールがあるんですが,うまく動かない気がします。


さて,後者についてですが,eval {} とかで例外を自前トラップしていても,Apache2::Reload がリロードしたときはトラップできないので,結局サーバのエラーログを見なくてはいけなくなるので面倒くさいです。Apache2::Reload を書き換えて,require エラー発生時に eval でトラップして画面に出力するように改造することも考えましたが,普通 InitHandler 等のハンドラとして指定するので Response フェーズより前なため,画面に出力できないのです*1

ということで悩んでいたんですが,とりあえず PostReadRequest (or HeaderParser) フェーズではそのまま成功したように返しておけば,Response フェーズでもっかい use したときにエラーが発生するんじゃね?と思い試してみたら,うまくいきました。

--- lib/Apache2/Reload.pm.orig  2005-10-21 09:04:43.000000000 +0900
+++ lib/Apache2/Reload.pm       2006-05-29 19:37:02.000000000 +0900
@@ -157,7 +157,11 @@
         if ($mtime > $Stat{$file}) {
             my $package = module_to_package($key);
             ModPerl::Util::unload_package($package);
-            require $key;
+            eval { require $key; };
+            if ($@) {
+                warn("Apache2::Reload reloading $package failed: $@\n");
+                ModPerl::Util::unload_package($package);
+            }
             warn("Apache2::Reload: process $$ reloading $package from $key\n")
                     if $DEBUG;
         }

こんな感じのパッチ。しつこく ModPerl::Util::unload_package() しているところがポイント。もちろん本物の Apache2::Reload.pm を置き換えないでください。あと,自前でエラートラップしている人しかありがたみがわからないと思います(すごくニッチ…)。
今のところうまく動いていますが,なにか副作用があるかもしれません。

追記 2006/05/30

うは。やっぱりいまいちうまくないかんじです。

*1:そもそも ResponseHandler で指定できるような Reloader を書いた方がいいのかも。何かパフォーマンス以外の理由で弊害とかありますかねぇ。