-
-
Save maxmilton/e2338b02b7381fc7bef2ccd96f434201 to your computer and use it in GitHub Desktop.
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Asynchronous Sentry JavaScript Error Tracking</title> | |
<!-- putting your CSS near the top is always a good idea --> | |
<link rel="stylesheet" href="/app.css"></link> | |
<!-- | |
load raven.js asynchronously | |
NOTE: crossorigin is recommended on any external scripts so you can capture | |
errors they throw correctly and to prevent sending credentials when fetching. | |
--> | |
<script async src="https://cdn.ravenjs.com/3.22.0/raven.min.js" crossorigin id="raven"></script> | |
<!-- | |
resolve sentry.io DNS and do TLS+TCP handshakes so the first request is faster | |
NOTE: If you're using the self-hosted version of Sentry then replace this URL | |
with your sentry endpoint. | |
--> | |
<link rel="preconnect" href="https://sentry.io"> | |
</head> | |
<body> | |
<div id="app"></div> | |
<!-- Javascript error tracking with Sentry --> | |
<script> | |
/* global Raven *//* eslint-disable func-names, strict, no-param-reassign, no-multi-assign, prefer-arrow-callback */ | |
(function (window, queue, loaded, script) { | |
'use strict'; | |
/** | |
* Optionally expose the loaded state and error queue globally to push | |
* errors from your app. | |
*/ | |
// window.sentry.loaded = loaded; | |
// window.sentry.queue = queue; | |
// capture and store any errors before raven.js is loaded | |
window.onerror = function e(message, file, lineNo, columnNo, error) { | |
if (loaded) return; | |
queue.push([message, file, lineNo, columnNo, error]); | |
}; | |
/** | |
* raven.js doesn't capture unhandled Promise rejections by default due | |
* to limited compatibility; only Chrome 49+ and some promise libraries | |
* (e.g. core-js, bluebird) are known to trigger this event. | |
*/ | |
window.onunhandledrejection = function e(error) { | |
if (loaded) return; | |
queue.push([ | |
error.reason.reason || error.reason.message, | |
error.type, | |
JSON.stringify(error.reason), | |
]); | |
}; | |
/** | |
* Optionally also track handled Promise rejections (useful for development | |
* or testing environments). Replace the above onunhandledrejection with: | |
*/ | |
// window.onunhandledrejection = window.onunhandledrejection = | |
// function e(error) { | |
// if (loaded) return; | |
// queue.push([ | |
// error.reason.reason || error.reason.message, | |
// error.type, | |
// JSON.stringify(error.reason), | |
// ]); | |
// }; | |
// once raven.js is loaded then install | |
script.onreadystatechange = script.onload = function () { | |
if (loaded) return; | |
loaded = true; | |
Raven.config('___PUBLIC_DSN___', { | |
environment: '<%= process.env.NODE_ENV %>', // if the page is a lodash template then the value is injected | |
release: '<%= process.env.APP_RELEASE %>', // same as above, these are just an example | |
tags: { app: 'example' }, | |
}).install(); | |
// same compatibility caveats as the above Promise error events | |
window.onunhandledrejection = function e(error) { | |
Raven.captureException(new Error(error.reason.reason || error.reason.message), { | |
extra: { | |
type: error.type, | |
reason: JSON.stringify(error.reason), | |
}, | |
}); | |
}; | |
/** Alternatively also track handled Promise rejections: */ | |
// window.onrejectionhandled = window.onunhandledrejection = | |
// function e(error) { | |
// Raven.captureException(new Error(error.reason.reason || error.reason.message), { | |
// extra: { | |
// type: error.type, | |
// reason: JSON.stringify(error.reason), | |
// }, | |
// }); | |
// }; | |
// report any previously stored errors | |
queue.forEach(function (error) { | |
/** | |
* error[0] = message | |
* error[1] = file, url, or type | |
* error[2] = line number or event | |
* error[3] = column number (old browsers may not emit this) | |
* error[4] = Error Object (old browsers may not emit this either!) | |
*/ | |
Raven.captureException(error[4] || new Error(error[0]), { | |
extra: { | |
file: error[1], | |
line: error[2], | |
col: error[3], | |
}, | |
}); | |
}); | |
}; | |
}(window, [], false, document.getElementById('raven'))); | |
</script> | |
<!-- your JavaScript should come after the above code so we can its track errors --> | |
<script src="/vendor.js"></script> | |
<script src="/app.js"></script> | |
</body> | |
</html> |
The problem with this approach is that the point of reference is the captureException
not the error itself (in Sentry, using the source map).
@phaistonian yeah I agree it's actually a rather big downside of this kind of approach. Better than nothing and definitely better than blocking the page load waiting for the sync script, at least in my case.
Do you have any ideas on how to report the error better, with it's true origin?
Great gist, thanks for sharing.
Keep in mind that Promises can reject with a reason of any type (not just another Reason or an Error object).
https://developer.mozilla.org/en-US/docs/Web/API/PromiseRejectionEvent/reason
Unless you're sure that all your Promises rejecting with objects, it might make sense to do something like this:
var reason = error.reason;
if (reason) reason = reason.reason || reason.message || reason;
queue.push([
reason,
...
Correct me if I am wrong, but for recents versions of SDK you can use it like this:
<script src="https://browser.sentry-cdn.com/5.6.1/bundle.min.js" integrity="sha384-pGTFmbQfua2KiaV2+ZLlfowPdd5VMT2xU4zCBcuJr7TVQozMO+I1FmPuVHY3u8KB" crossorigin="anonymous" async id="sentry-sdk"></script>
<script>
document.getElementById('sentry-sdk').addEventListener('load', function () {
Sentry.init({ dsn: 'https://<key>@sentry.io/<project>' });
}, false);
</script>
If you prefer a quick copy+paste, here's the minified version:
Replace
___PUBLIC_DSN___
with your DSN and paste this somewhere in the head near your closing</head>
tag. Or if you're a hipster who doesn't use<head>
and<body>
tags anymore then just paste it near the top after any critical/app resources (e.g. CSS). Ideally it should be before any other JavaScript so you can capture errors from scripts loaded after this code.