When this happens to your users, you want to know about it.

Logging Client Side JavaScript stack traces

Peter Mikitsh
2 min readSep 17, 2017

When creating single-page applications, it’s a fact that you’re going to ship code that has unexpected behavior. Stay one step ahead by logging the stack traces of uncaught errors and promises.

The two API’s for capturing uncaught errors and promises are, respectively:

  • window.onerror
  • window.onunhandledrejection

Let’s go through setting up handlers for both API’s. If your production bundle is minified, and source maps aren’t publicly exposed, I’ll also show how to transform the stack traces server-side.

window.onerror

Although Error.protoytype.stack is non-standard, it is implemented by browsers IE10+ and Safari 6+, which is sufficient for many production-grade web applications. While there are inconsistencies between browsers, libraries like error-stack-parser deal with the nuances for you.

window.onerror = function onError(msg, file, line, col, error) {
if (error && error.stack) {
/*
* POST error.stack to your server
*/
}
};

window.onunhandledrejection

Handling promises is less straightforward. The standard Promise API shipping with Chrome doesn’t include stack traces. Fortunately, bluebird has support for nice stack traces. In your client application, override the global Promise and enable longStackTraces like so:

global.Promise = require('bluebird');Promise.config({
longStackTraces: true
});

And add the handler:

window.onunhandledrejection = function onUnhandledRejection(event) {
if (
event &&
event.detail &&
event.detail.reason &&
event.detail.reason.stack
) {
/*
* POST event.detail.reason.stack to your server
*/
}
};

Applying sourcemaps to stack traces

If your sourcemaps aren’t available on the client, likely for good security reasons, you’ll need to map the minified stack traces back to your source code using sourcemaps.

A handy library called retrace makes applying source maps to stack traces simple. Load your source map, register it with retrace, and invoke retrace’s map API (retrace’s documentation covers more advanced use cases, such as handling support for multiple hostnames).

import fs from 'fs';
import retrace from 'retrace';
const sourceMap = fs.readFileSync('./bundle.js.map', 'utf8');
retrace.register('https://example.com/bundle.min.js', sourceMap);
/* stack trace from our POST request */
function reportError(stacktrace) {
retrace
.map(stacktrace)
.then((mappedStackTrace) => {
/*
* Log mappedStackTrace to your logging service
*/
})
.catch(e) => {
console.log('Unable to apply sourcemaps', e);
});
}

Happy logging! You can now know have some insight into your end users’ experience with your SPA.

--

--