トップ頁
先頭
最後

エラーが発生!!何をしたんですか?

プログラムがバグったら!?!!

怒られます。めっちゃ怒られます。

そして、バグ対(策)のついでに仕変(仕様変更)をこっそり(無料で)やらされます(当然ながら、文句を言える立場ではありません)。

さて、1時間経っても原因不明の場合は、どうなるでしょう。

お前らなんぞ、ゴミだ糞だと、めちゃくちゃ言われます。もちろん、上司は客に言われたイライラを10倍にして部下に投げつけます。

このようなことを繰り返すうち、いつしか精神を病み、人生をリタイアすることになるのです。

怖いですね〜。

このような惨事を避けるのは簡単です。バグのないプログラムを作れば良いのです。<無理!(常識あるものの声)

どんな状況であれ、仕様書に記述されている内容は、全て網羅し正しく動作するプログラムです。<不可能!(デスマーチを歩んだものの声)

仕様書に記述されていない内容も、全てが顧客が使いやすいように、熟慮の結果実装されているのです。<出来っこない(真実を知るものの声)

起こりうるシステム上の問題点も、全て考慮して作れば...(パン)...乾いた破裂音が、室内に響き渡った。

バグのないプログラムなど作れるわけが無いので(努力はしますよもちろん)、次善の策を考えます。

どこ?

原因究明の第一歩は、そう「どこ?」です。

それさえわかれば、「俺が作ったとこじゃないから関係ないんで、お先に!」と言って帰れるかもしれません。

それさえわかれば、何が原因となって問題が起きたかを、判別できるかもしれません。逆に、「どこ?」が分からないと、原因の判別は...

どこ?を見つける、その前に

JavaScriptの例外についておさらい

JavaScriptでエラーが発生して(というより、エラーの状態になって)処理を続行できない場合、例外を投げて処理を終了させます。

JavaScriptで例外
 1:  if (おっとっとの状態?) {
 2:      throw new Error("おっとっとの状態ですよ!");
 3:  }

通常は、これをキャッチして、「何らかの対処」をします。

JavaScriptで例外
 1:  try {
 2:      /* 何かの処理 */
 3:  } catch (e) {
 4:      /* 何らかの対処 */
 5:  }

で「何らかの処理」とは、何が出来るのでしょう。私の環境(firefox)では、次のようなコードで、エラーが発生した箇所を特定できます。

例外が発生した箇所を見つける
 1:  try {
 2:      /* 何かの処理 */
 3:  } catch (e) {
 4:      alert(e.name + '\n' +
 5:            e.message + '\n' +
 6:            e.fileName + ':' + e.lineNumber + '\n' +
 7:            e.stack);
 8:  }

e.nameは、投げられた例外のタイプです。ECMAScriptでは、Error以外にも、以下のようないくつかの例外クラスを用意しており、そのクラス名が格納されています。

EvalError
eval()関数に関するエラーです。
RangeError
数値変数の有効範囲に関連するエラーです。
ReferenceError
不正な参照の読み出しで発生するエラーです。
SyntaxError
eval()関数内のコードの構文エラーで発生するエラーです。
TypeError
変数の型不正などで発生するエラーです
URIError
URIの処理を行う関数に、有効ではないURIを渡した時などに発生するエラーです。

※エラー内容の説明は、Mozilla Developer Networkを参考にさせて頂きました。

e.messageは、Error作成時に指定したメッセージです。

e.fileNamee.lineNumberは、それぞれErrorを作成したスクリプトコードのファイル名と行番号です。

e.stackは、Errorを作成するまでのスタックトレースです。

これだけの情報があれば、エラーが発生した場所の特定はできるので完璧です。少なくとも私の環境であれば。

しかし、標準としてサポートされているのはnamemessageだけなので、どこで発生したErrorなのかを特定することは、標準の機能だけではできません。

これには、完璧とは言えませんが簡単な解決策があります。window.onerrorイベントをハンドリングすることです。

window.onerrorイベントのハンドリング
 1:  window.onerror = function (message, url, line) {
 2:      alert(message + '\n' +
 3:            url + ':' + line);
 4:      return true;
 5:  }

これは、スクリプト中の全てのキャッチしていない例外を検知して通知してくれるので便利です。引数として渡されるのは、Error作成時に指定したメッセージ・エラーが発生したファイル名及び行番号です。

このハンドラからtrueを返すことで、Webブラウザに対してデフォルトのエラー処理(エラーが発生したというダイアログの表示や、コンソールへのエラーログの出力等)を抑止させることができます。

この処理方式は、主なWebブラウザの最近のバージョンであれば、ほぼ適用できます。

Webブラウザonerror
サポート
バージョン
messageurllinetrue/false備考
IE5.5 以上
Firefox6.1 以上
Google chrome13.0 以上
Opera11.60 以上
Safari5.1 以上設定→詳細で、「メニューに“開発”メニューを表示」にチェックを付けて、再起動するとurlとlineが取得できるようになる。

※バージョン等は、Web上の情報によります。実際にテストした結果では有りません。

※確認したOSは、FirefoxとGoogle chromeは、Linux・WindowsXP・Windows7、それ以外は、WindowsXP・Windows7。

但し、取得できる行番号については注意が必要です。

HTMLサンプル
 1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 2:     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 3: <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
 4:     <head>
 5:         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 6:         <title>onerrorのテスト</title>
 7:         <script type="text/javascript">
 8:             function errfunc1() {
 9:                 errfunc2();
10:             }
11:             function errfunc2() {
12:                 errfunc3();     // ここでエラーが発生する
13:             }
14:             window.onerror = function (message, url, line) {
15:                 alert(message + '\n' + url + ':' + line);
16:                 return true;
17:             }
18:         </script>
19:     </head>
20:     <body>
21:         <p><button onclick="errfunc1()">エラーを発生させる</button></p>
22:     </body>
23: </html>

上記のようなコードでは、殆どのWebブラウザが、エラー発生行に12が設定されますが、Operaでは6が設定されます(7行目の<script type="text/javascript">が起点になっている)。

しかし、それ以上に問題なのは、下記のように一旦キャッチした例外を再度スローしているケースです。

HTMLサンプル
 1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 2:     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 3: <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
 4:     <head>
 5:         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 6:         <title>onerrorのテスト</title>
 7:         <script type="text/javascript">
 8:             function errfunc1() {
 9:                 try {
10:                     errfunc2();
11:                 } catch (e) {
12:                     throw e;
13:                 }
14:             }
15:             function errfunc2() {
16:                 errfunc3();     // ここでエラーが発生する
17:             }
18:             window.onerror = function (message, url, line) {
19:                 alert(message + '\n' + url + ':' + line);
20:                 return true;
21:             }
22:         </script>
23:     </head>
24:     <body>
25:         <p><button onclick="errfunc1()">エラーを発生させる</button></p>
26:     </body>
27: </html>

当然、エラー発生行には16が設定されて欲しいのですが、そうは問屋が下ろしません(死語?)。

IEとFirefoxをSafariは、16を設定してくれるのですが、Google chromeは12、Operaは6を設定します。このようなときだけは、IEの高い占有率が頼もしいものです。

「殆どのWebブラウザで、大丈夫です。」と言えるのですから。

どこ?の次は

これで、殆どのWebブラウザで、エラーが発生した箇所を見つけることが出来るようになりました。

しかし、これだけでは情報が少なすぎます。例えば、共通のチェックメソッドでエラーになったことが判ったとしても、何をチェックしたのかが判らなければ、意味がありません。

どのようにしてエラーとなるに至ったかを、知る必要があるのです。

この続きは、また後ほど。