Skip to content

Instantly share code, notes, and snippets.

@eyecatchup
Created July 1, 2019 08:34
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save eyecatchup/d0e1fb062343d45fbb5800dd0dc3d4d9 to your computer and use it in GitHub Desktop.
Save eyecatchup/d0e1fb062343d45fbb5800dd0dc3d4d9 to your computer and use it in GitHub Desktop.
JavaScript global error handling
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);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment