JavaScriptグローバルイベントメカニズム


350

スローされるすべての未定義の関数エラーをキャッチしたいと思います。JavaScriptにグローバルエラー処理機能はありますか?使用例は、定義されていないフラッシュからの関数呼び出しをキャッチすることです。


エラーを見つけたら、どうしますか?不足している関数を作成できるようにログに記録する必要があるだけですか、それとも例外がコードを壊すのを止めたいですか?
ダンハーバート

2
不足している関数の名前を取得し、文字列の存在に基づいて自分の関数を呼び出したいのですが。文字列 'close'を含む関数への呼び出しは、たとえばmy close()を呼び出します。また、その時点でエラーをトラップします。

2
exceptionsjs.comはこの機能を提供し、「ガード」機能を使用して未定義の機能に関連するエラーのみをキャッチするように調整できます。
Steven Wexler 2014

回答:


192

これはあなたを助けますか:

<script type="text/javascript">
window.onerror = function() {
    alert("Error caught");
};

xxx();
</script>

Flashエラーの処理方法はわかりません...

更新:Operaでは機能しませんが、現在Dragonflyをハッキングして、何が得られるかを確認しています。Dragonflyのハッキングに関する提案は、次の質問にあります。

模倣ウィンドウ。JavaScriptを使用したOperaのonerror


3
msg、file_loc、line_no paramsを追加すると、これでうまくいくはずです。ありがとう!

1
このコードは既存のエラーハンドラーをオーバーライドすることを正しく理解していますか?
Mars Robertson

@MarsRobertson No.
atilkan

@atilkan window.onerror = function() { alert(42) };が答えのコードになりました:window.onerror = function() { alert("Error caught"); };オーバーライドされない、私はまだわからないよ...
火星ロバートソン

@MarsRobertsonわかりました。おそらく上書きします。はい、テストしました。addEventListenerを使用する方が良いでしょう。
atilkan

646

未処理のJavascriptエラーをキャッチする方法

次のwindow.onerrorようなイベントハンドラーにイベントを割り当てます。

<script type="text/javascript">
window.onerror = function(msg, url, line, col, error) {
   // Note that col & error are new to the HTML 5 spec and may not be 
   // supported in every browser.  It worked for me in Chrome.
   var extra = !col ? '' : '\ncolumn: ' + col;
   extra += !error ? '' : '\nerror: ' + error;

   // You can view the information in an alert to see things working like this:
   alert("Error: " + msg + "\nurl: " + url + "\nline: " + line + extra);

   // TODO: Report this error via ajax so you can keep track
   //       of what pages have JS issues

   var suppressErrorAlert = true;
   // If you return true, then error alerts (like in older versions of 
   // Internet Explorer) will be suppressed.
   return suppressErrorAlert;
};
</script>

コードでコメントされているように、の戻り値window.onerrorがのtrue場合、ブラウザは警告ダイアログの表示を抑制します。

window.onerrorイベントはいつ発生しますか?

簡単に言えば、イベントは、1。)キャッチされない例外があるか、2。)コンパイル時エラーが発生したときに発生します。

キャッチされない例外

  • 「いくつかのメッセージ」を投げる
  • call_something_undefined();
  • cross_origin_iframe.contentWindow.document ;、セキュリティ例外

コンパイルエラー

  • <script>{</script>
  • <script>for(;)</script>
  • <script>"oops</script>
  • setTimeout("{", 10);、最初の引数をスクリプトとしてコンパイルしようとします

window.onerrorをサポートするブラウザー

  • Chrome 13以降
  • Firefox 6.0以降
  • Internet Explorer 5.5以降
  • Opera 11.60以降
  • Safari 5.1以降

スクリーンショット:

これをテストページに追加した後の上記のonerrorコードの例:

<script type="text/javascript">
call_something_undefined();
</script>

window.onerrorイベントによって詳細に示されたエラー情報を示すJavascriptアラート

AJAXエラー報告の例

var error_data = {
    url: document.location.href,
};

if(error != null) {
    error_data['name'] = error.name; // e.g. ReferenceError
    error_data['message'] = error.line;
    error_data['stack'] = error.stack;
} else {
    error_data['msg'] = msg;
    error_data['filename'] = filename;
    error_data['line'] = line;
    error_data['col'] = col;
}

var xhr = new XMLHttpRequest();

xhr.open('POST', '/ajax/log_javascript_error');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
    if (xhr.status === 200) {
        console.log('JS error logged');
    } else if (xhr.status !== 200) {
        console.error('Failed to log JS error.');
        console.error(xhr);
        console.error(xhr.status);
        console.error(xhr.responseText);
    }
};
xhr.send(JSON.stringify(error_data));

JSFiddle:

https://jsfiddle.net/nzfvm44d/

参照:


3
throw手動で作成した場合、Firefoxはエラーメッセージを返さないことに言及する価値があります。stackoverflow.com/questions/15036165/...
Sebas

2
すばらしい答えです。JSNLogのようなパッケージを使用して、「ajaxを介してこのエラーを報告する」を実装できます。これは、ajaxおよびサーバー側のロギングを実行するJSNLogのようなパッケージです。
user1147862 2013年

4
この回答に加えて、スタックトレースを取得できるようにerrオブジェクトを追加しました。stackoverflow.com/a/20972210/511438。私の開発エラーがページの上部にボックスとして表示されているので(これを作成したため)、より多くのフィードバックを使用して開発できます。
Valamas 2014年

onerror()がさらにエラーをスローしないようにして、onerrorの実装をtry-catch(ignore)ブロックにパックする方がよいのではないでしょうか。(ここではそうではありませんが、念のため)
Pieter De Bie

1
@ super1ha1 jsfiddleを提供できますか?ブラウザがonerrorイベントハンドラーがイベントの発生を停止するという重大な変更を導入するとは思わない。このjsfiddleを試して、onerrorハンドラーが機能していることを確認できます。jsfiddle.net/ nzfvm44d これは、Chromeバージョン62.0.3202.94(公式ビルド)(64ビット)でも動作します。
サム

38

洗練されたエラー処理

エラー処理が非常に洗練されていて、それ自体がエラーをスローする可能性がある場合は、すでに「errorHandling-Mode」になっているかどうかを示すフラグを追加すると便利です。そのようです:

var appIsHandlingError = false;

window.onerror = function() {
    if (!appIsHandlingError) {
        appIsHandlingError = true;
        handleError();
    }
};

function handleError() {
    // graceful error handling
    // if successful: appIsHandlingError = false;
}

そうしないと、無限ループに陥る可能性があります。


29
または、より安全な方法は、handleErrorメソッドの周りでtry-catchを使用することです。
アイディアカピ

8
エラー処理で非同期呼び出しがある場合は、try catchを使用できません。したがって、彼の解決策は依然として優れた解決策です
Emrys Myrooin、2015

@EmrysMyrooinそれでも、エラー処理によって同期または非同期ループが発生し、使用可能なスタック情報がないとクラッシュする可能性があります。
inf3rno 2016

1
フラグはいつかリセットされるべきだと思いますよね?if if if if if / catch around if if if、およびフラグがonerrorメソッドを終了する直前にリセットされることを確認してください。それ以外の場合は、1つのエラーのみが処理されます。
2017年

23

最新のWebアプリに高度なエラートラッキングとリアルユーザーモニタリングを提供するAtatusをお試しください。

https://www.atatus.com/

すべてのブラウザで合理的に完全なスタックトレースを取得する方法を説明しましょう。

JavaScriptでのエラー処理

最新のChromeおよびOperaは、ErrorEventおよびのHTML 5ドラフト仕様を完全にサポートしていますwindow.onerror。これらのブラウザーの両方で、を使用するかwindow.onerror、 'error'イベントに適切にバインドできます。

// Only Chrome & Opera pass the error object.
window.onerror = function (message, file, line, col, error) {
    console.log(message, "from", error.stack);
    // You can send data to your server
    // sendError(data);
};
// Only Chrome & Opera have an error attribute on the event.
window.addEventListener("error", function (e) {
    console.log(e.error.message, "from", e.error.stack);
    // You can send data to your server
    // sendError(data);
})

残念ながら、Firefox、Safari、IEはまだ存在しており、それらもサポートする必要があります。スタックトレースは利用できwindow.onerrorないため、もう少し作業を行う必要があります。

エラーからスタックトレースを取得するために私たちができる唯一のことは、すべてのコードをtry{ }catch(e){ }ブロックにラップしてから調べることe.stackです。関数を受け取り、適切なエラー処理を備えた新しい関数を返すwrapと呼ばれる関数を使用すると、処理を多少簡単にすることができます。

function wrap(func) {
    // Ensure we only wrap the function once.
    if (!func._wrapped) {
        func._wrapped = function () {
            try{
                func.apply(this, arguments);
            } catch(e) {
                console.log(e.message, "from", e.stack);
                // You can send data to your server
                // sendError(data);
                throw e;
            }
        }
    }
    return func._wrapped;
};

これは機能します。手動でラップする関数はすべて適切なエラー処理を備えていますが、ほとんどの場合、実際にそれを自動的に行うことができます。

addEventListenerコールバックを自動的にラップするようにのグローバル定義を変更することにより、try{ }catch(e){ }ほとんどのコードの周りに自動的に挿入できます。これにより、既存のコードは引き続き機能しますが、高品質の例外追跡が追加されます。

var addEventListener = window.EventTarget.prototype.addEventListener;
window.EventTarget.prototype.addEventListener = function (event, callback, bubble) {
    addEventListener.call(this, event, wrap(callback), bubble);
}

また、それremoveEventListenerが機能し続けることを確認する必要もあります。現時点では、への引数addEventListenerが変更されたため、そうなりません。この場合も、prototypeオブジェクトでこれを修正するだけです。

var removeEventListener = window.EventTarget.prototype.removeEventListener;
window.EventTarget.prototype.removeEventListener = function (event, callback, bubble) {
    removeEventListener.call(this, event, callback._wrapped || callback, bubble);
}

エラーデータをバックエンドに送信する

次のようにイメージタグを使用してエラーデータを送信できます

function sendError(data) {
    var img = newImage(),
        src = 'http://yourserver.com/jserror&data=' + encodeURIComponent(JSON.stringify(data));

    img.crossOrigin = 'anonymous';
    img.onload = function success() {
        console.log('success', data);
    };
    img.onerror = img.onabort = function failure() {
        console.error('failure', data);
    };
    img.src = src;
}

免責事項:私はhttps://www.atatus.com/の Web開発者です。


yourserver.com/jserrorとはですか?REST、Webサービス、Wcfサービス?バックエンドについて簡単なことはありますか?
Kiquenet 2015

ユーザーのブラウザーからサーバーにjsエラーを送信する場合。http://yourserver.com受信して保存するには、バックエンド()を記述する必要があります。atatus.comを選択した場合、何もする必要はありません。ページに2行のスクリプトを含めるだけです。
Fizer Khan、2015

18

window.onerror考えられるすべてのエラーへのアクセスが提供されているわけではないようです。特にそれは無視します:

  1. <img> 読み込みエラー(応答> = 400)。
  2. <script> 読み込みエラー(応答> = 400)。
  3. アプリに他の多くのライブラリwindow.onerrorがあり、未知の方法(jquery、angularなど)で操作している場合、グローバルエラー。
  4. たぶん、これを調べた後でまだ実行していない多くのケース(iframe、スタックオーバーフローなど)。

これらのエラーの多くをキャッチするスクリプトの開始点を以下に示します。これにより、開発中にアプリにより強力なデバッグを追加できます。

(function(){

/**
 * Capture error data for debugging in web console.
 */

var captures = [];

/**
 * Wait until `window.onload`, so any external scripts
 * you might load have a chance to set their own error handlers,
 * which we don't want to override.
 */

window.addEventListener('load', onload);

/**
 * Custom global function to standardize 
 * window.onerror so it works like you'd think.
 *
 * @see http://www.quirksmode.org/dom/events/error.html
 */

window.onanyerror = window.onanyerror || onanyerrorx;

/**
 * Hook up all error handlers after window loads.
 */

function onload() {
  handleGlobal();
  handleXMLHttp();
  handleImage();
  handleScript();
  handleEvents();
}

/**
 * Handle global window events.
 */

function handleGlobal() {
  var onerrorx = window.onerror;
  window.addEventListener('error', onerror);

  function onerror(msg, url, line, col, error) {
    window.onanyerror.apply(this, arguments);
    if (onerrorx) return onerrorx.apply(null, arguments);
  }
}

/**
 * Handle ajax request errors.
 */

function handleXMLHttp() {
  var sendx = XMLHttpRequest.prototype.send;
  window.XMLHttpRequest.prototype.send = function(){
    handleAsync(this);
    return sendx.apply(this, arguments);
  };
}

/**
 * Handle image errors.
 */

function handleImage() {
  var ImageOriginal = window.Image;
  window.Image = ImageOverride;

  /**
   * New `Image` constructor. Might cause some problems,
   * but not sure yet. This is at least a start, and works on chrome.
   */

  function ImageOverride() {
    var img = new ImageOriginal;
    onnext(function(){ handleAsync(img); });
    return img;
  }
}

/**
 * Handle script errors.
 */

function handleScript() {
  var HTMLScriptElementOriginal = window.HTMLScriptElement;
  window.HTMLScriptElement = HTMLScriptElementOverride;

  /**
   * New `HTMLScriptElement` constructor.
   *
   * Allows us to globally override onload.
   * Not ideal to override stuff, but it helps with debugging.
   */

  function HTMLScriptElementOverride() {
    var script = new HTMLScriptElement;
    onnext(function(){ handleAsync(script); });
    return script;
  }
}

/**
 * Handle errors in events.
 *
 * @see http://stackoverflow.com/questions/951791/javascript-global-error-handling/31750604#31750604
 */

function handleEvents() {
  var addEventListenerx = window.EventTarget.prototype.addEventListener;
  window.EventTarget.prototype.addEventListener = addEventListener;
  var removeEventListenerx = window.EventTarget.prototype.removeEventListener;
  window.EventTarget.prototype.removeEventListener = removeEventListener;

  function addEventListener(event, handler, bubble) {
    var handlerx = wrap(handler);
    return addEventListenerx.call(this, event, handlerx, bubble);
  }

  function removeEventListener(event, handler, bubble) {
    handler = handler._witherror || handler;
    removeEventListenerx.call(this, event, handler, bubble);
  }

  function wrap(fn) {
    fn._witherror = witherror;

    function witherror() {
      try {
        fn.apply(this, arguments);
      } catch(e) {
        window.onanyerror.apply(this, e);
        throw e;
      }
    }
    return fn;
  }
}

/**
 * Handle image/ajax request errors generically.
 */

function handleAsync(obj) {
  var onerrorx = obj.onerror;
  obj.onerror = onerror;
  var onabortx = obj.onabort;
  obj.onabort = onabort;
  var onloadx = obj.onload;
  obj.onload = onload;

  /**
   * Handle `onerror`.
   */

  function onerror(error) {
    window.onanyerror.call(this, error);
    if (onerrorx) return onerrorx.apply(this, arguments);
  };

  /**
   * Handle `onabort`.
   */

  function onabort(error) {
    window.onanyerror.call(this, error);
    if (onabortx) return onabortx.apply(this, arguments);
  };

  /**
   * Handle `onload`.
   *
   * For images, you can get a 403 response error,
   * but this isn't triggered as a global on error.
   * This sort of standardizes it.
   *
   * "there is no way to get the HTTP status from a 
   * request made by an img tag in JavaScript."
   * @see http://stackoverflow.com/questions/8108636/how-to-get-http-status-code-of-img-tags/8108646#8108646
   */

  function onload(request) {
    if (request.status && request.status >= 400) {
      window.onanyerror.call(this, request);
    }
    if (onloadx) return onloadx.apply(this, arguments);
  }
}

/**
 * Generic error handler.
 *
 * This shows the basic implementation, 
 * which you could override in your app.
 */

function onanyerrorx(entity) {
  var display = entity;

  // ajax request
  if (entity instanceof XMLHttpRequest) {
    // 400: http://example.com/image.png
    display = entity.status + ' ' + entity.responseURL;
  } else if (entity instanceof Event) {
    // global window events, or image events
    var target = entity.currentTarget;
    display = target;
  } else {
    // not sure if there are others
  }

  capture(entity);
  console.log('[onanyerror]', display, entity);
}

/**
 * Capture stuff for debugging purposes.
 *
 * Keep them in memory so you can reference them
 * in the chrome debugger as `onanyerror0` up to `onanyerror99`.
 */

function capture(entity) {
  captures.push(entity);
  if (captures.length > 100) captures.unshift();

  // keep the last ones around
  var i = captures.length;
  while (--i) {
    var x = captures[i];
    window['onanyerror' + i] = x;
  }
}

/**
 * Wait til next code execution cycle as fast as possible.
 */

function onnext(fn) {
  setTimeout(fn, 0);
}

})();

次のように使用できます。

window.onanyerror = function(entity){
  console.log('some error', entity);
};

完全なスクリプトには、受信したエンティティ/エラーの半読み取り可能な「表示」バージョンを出力しようとするデフォルトの実装があります。アプリ固有のエラーハンドラーのインスピレーションとして使用できます。デフォルトの実装では、最後の100個のエラーエンティティへの参照も保持されるため、次のように発生した後、Webコンソールでそれらを検査できます。

window.onanyerror0
window.onanyerror1
...
window.onanyerror99

注:これは、いくつかのブラウザー/ネイティブコンストラクターのメソッドをオーバーライドすることで機能します。これには、意図しない副作用が生じる可能性があります。ただし、開発中に使用したり、エラーが発生している場所を特定したり、開発中にNewRelicやSentryなどのサービスにログを送信して、開発中にエラーを測定したり、ステージング中に何が起こっているのかをデバッグしたりするのに役立ちます。より深いレベル。その後、本番環境でオフにすることができます。

お役に立てれば。


1
どうやら画像はエラーイベントをトリガーします:stackoverflow.com/a/18152753/607033
inf3rno

6
// display error messages for a page, but never more than 3 errors
window.onerror = function(msg, url, line) {
if (onerror.num++ < onerror.max) {
alert("ERROR: " + msg + "\n" + url + ":" + line);
return true;
}
}
onerror.max = 3;
onerror.num = 0;

6

以前に関連付けられたonerrorコールバックも保持する必要があります

<script type="text/javascript">

(function() {
    var errorCallback = window.onerror;
    window.onerror = function () {
        // handle error condition
        errorCallback && errorCallback.apply(this, arguments);
    };
})();

</script>

3

キャッチされていないエラーと未処理のプロミスの拒否の両方を処理する統一された方法が必要な場合は、キャッチされていないライブラリを調べてください。

編集

<script type="text/javascript" src=".../uncaught/lib/index.js"></script>

<script type="text/javascript">
    uncaught.start();
    uncaught.addListener(function (error) {
        console.log('Uncaught error or rejection: ', error.message);
    });
</script>

ウィンドウをリッスンします。unhandledrejection window.onerrorに加えてインチ


2
上記の回答の Lance Pollardは通常のonerrorでは処理できないいくつかのエラーについて述べています。ライブラリはそれらを処理しますか?そのうちのいくつかを指定してください。
Michael Freidgeim

@ MiFreidgeimSO-stopbeingevilソースコードを確認したところ、残念ながらそうではありませんでした。Aleksandr Oleynikov:あなたがこれをサポートできれば素晴らしいと思います-その後、私は反対票を賛成票に変えます;-)
brillout

2

Trackjsを試してみることをお勧めします。

これはサービスとしてのエラーロギングです。

セットアップは驚くほど簡単です。各ページに1つの<script>行を追加するだけです。これは、気に入らない場合は簡単に削除できることも意味します。

Sentry(独自のサーバーをホストできる場合はオープンソースです)のような他のサービスもありますが、Trackjsが行うことはしません。Trackjsは、ブラウザーとWebサーバー間のユーザーの相互作用を記録するため、ファイルと行番号の参照だけでなく(おそらくスタックトレース)、エラーを引き起こしたユーザーステップを実際に追跡できます。


2
無料試用版がありますが、TrackJSには無料なくなったようです
15年

これは追跡してエラーを警告するのには問題ありませんが、実際には処理部分を解決しません。理想的には、質問者はこれらのエラーを処理して残りのコードが実行されるようにする方法を探していると思います。
wgp 2015

エラーイベントをキャッチし、スタックトレースとアプリケーションの状態でxhr呼び出しをロガーに追加するとどうなりますか?trackjsはどのように優れていますか?
inf3rno 2016

2
@ inf3rnoエラーの始まりを追跡するだけのスタックトレースではありません。TrackJSはユーザーセッション全体を追跡するので、その原因を確認できます。ここでは例として、スクリーンショットだtrackjs.com/assets/images/screenshot.pngは
ケイン

1

関数をwindow.onerrorに割り当てることにより、onerrorイベントをリッスンします。

 window.onerror = function (msg, url, lineNo, columnNo, error) {
        var string = msg.toLowerCase();
        var substring = "script error";
        if (string.indexOf(substring) > -1){
            alert('Script Error: See Browser Console for Detail');
        } else {
            alert(msg, url, lineNo, columnNo, error);
        }   
      return false; 
  };
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.