Skip to content

Instantly share code, notes, and snippets.

@eyecatchup
Created July 1, 2019 08:34
Show Gist options
  • Save eyecatchup/d0e1fb062343d45fbb5800dd0dc3d4d9 to your computer and use it in GitHub Desktop.
Save eyecatchup/d0e1fb062343d45fbb5800dd0dc3d4d9 to your computer and use it in GitHub Desktop.

Revisions

  1. eyecatchup created this gist Jul 1, 2019.
    98 changes: 98 additions & 0 deletions catch-js-error-stacktrace.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,98 @@
    var sendError = function (err) {
    console.log('Caught JS client error:');
    console.dir(err);

    var xhr = new XMLHttpRequest();
    xhr.open('POST', '/api/error/add', true);
    xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');
    xhr.send(JSON.stringify(err));
    };

    /**
    * Chrome and Opera fully support the HTML 5 draft spec for ErrorEvent and window.onerror.
    * In both of these browsers you can use window.onerror to get a full stacktrace:
    */
    var processError = function (msg, url, lineNo, columnNo, error) {
    var log = {};

    // Handle error objects (/w stack trace)
    if (msg && msg.message) {
    log.err = {
    'msg': msg.message,
    'file': !!msg.fileName ? msg.fileName : false,
    'ln': !!msg.lineNumber ? msg.lineNumber : !!lineNo ? lineNo : false,
    'col': !!msg.columnNumber ? msg.columnNumber : !!columnNo ? columnNo : false,
    'stacktrace': !!msg.stack ? msg.stack : false,
    'cause': !!url ? JSON.stringify(url) : false,
    'errorObj': !!error ? error : false
    };
    } else {
    log.err = {
    'msg': msg
    };

    if (!!url) {
    log.err.file = url;
    }
    }

    log.url = window.location.href;
    log.userAgent = window.navigator.userAgent;

    sendError(log);
    };

    window.onerror = function (exception, url, lineNo, columnNo, error) {
    processError(exception, url);
    return true; // surpress error alerts in old IEs
    };

    /**
    * Unfortunately Firefox, Safari and IE are still around and we have to support them too.
    * As the stacktrace is not available in window.onerror we have to do a little bit more work:
    * It turns out that the only thing we can do to get stacktraces from errors is to wrap all
    * of our code in a `try{ }catch(e){ }` block and then look at `e.stack`.
    */

    function wrap(func) {
    // Ensure we only wrap the function once.
    if (!func._wrapped) {
    func._wrapped = function () {
    try{
    func.apply(this, arguments);
    } catch(e) {
    var log = {};

    if (e && e.message) {
    log.err = {
    'msg': e.message,
    'stacktrace': !!e.stack ? e.stack : false
    };
    sendError(log);
    }

    throw e;
    }
    }
    }

    return func._wrapped;
    };

    /**
    * By changing the global definition of addEventListener so that it automatically wraps
    * the callback we can automatically insert `try{ }catch(e){ }` around most code.
    */
    var addEventListener = window.EventTarget.prototype.addEventListener;
    window.EventTarget.prototype.addEventListener = function (event, callback, bubble) {
    addEventListener.call(this, event, wrap(callback), bubble);
    }

    /**
    * We also need to make sure that removeEventListener keeps working. Because the argument
    * to addEventListener is changed, it won't. Again we only need to fix this on the prototype object:
    */
    var removeEventListener = window.EventTarget.prototype.removeEventListener;
    window.EventTarget.prototype.removeEventListener = function (event, callback, bubble) {
    removeEventListener.call(this, event, callback._wrapped || callback, bubble);
    }