Tuesday, 22 October 2013

Stack Traces, window.onerror, and the future

One argument that many people have made (and still make) is that window.onerror doesn't provide sufficient information to track down client-side JavaScript errors. While there's certainly some truth to it, I've always thought of it as a case of worse is better.

Consider the alternative: you'd have to wrap all your code in try/catch blocks. But that's not enough. Because of the nature of the event loop in browsers, you will also have to wrap all your async code in try/catch blocks as well. That includes all DOM event handlers, XHR and family including WebSockets, and setTimeout and family. That's something you'll just have to do. Because this requires modification of your code, it's terribly invasive. This also means that you will almost certainly miss catching errors in several cases, simply due to oversight. And that's still not saying anything about the performance overhead of working with try/catch blocks, not just in Chrome. All that, just to get some additional data.

Errorception lets you pass your error objects to us if you want to, since quite some time now. However, less than 10% of errors at Errorception are recorded using this method. It is obvious that the window.onerror approach works far better, either because try/catch isn't comprehensive, or because it is inconvenient to use.

window.onerror = function(message, fileName, lineno) { ... }

That's all the data you got from window.onerror: the error message, the URL of the script, and the line number. Errorception of course records much more information about the browser and its state for you automatically, so there's already a lot of context available.

But Errorception has sorely lacked a very vital piece of data to aid debugging: stack traces. Stack traces are trivial to extract from the error object you get in the catch block of a try/catch statement. Sure, there were a couple of tricks up our sleeve to get fake stack traces where possible, but those were severely limited.

Obviously this problem wasn't one that just Errorception faced. The web community went to browser vendors and standards bodies with their torches and pitchforks (ok, it wasn't quite as dramatic as that), to ask for some love for window.onerror. A couple of months ago, the HTML spec finally added two new attributes for window.onerror.

window.onerror = function(message, fileName, lineno, colno, error) { ... }

See that last argument there? That's what's exciting. That's the error object you would have otherwise got if you had wrapped your code in try/catch blocks. That's right: You don't need to wrap your code in try/catch blocks anymore to get rich error information. This changes everything!

Browser support

As of this writing, no production browser supports these new attributes. But don't let that dishearten you – the spec is only about 3 months old after all. IE10 does support the colno attribute, but not the error object itself. That's because IE10's release predates the spec. I expect the next release of IE to have the error object supported. (Seriously, let's cut IE some slack. They've been doing some rocking work lately. They certainly took the lead here.) Chrome already rolled this out in Chrome Canary two months ago, so it should be be in a public release soon. Discussions are on in Firefox's Bugzilla, and I expect this to be resolved soon as well. The folk over at WebKit seem interested too, though admittedly progress has been slow.

Errorception and window.onerror

Needless to say, Errorception has now rolled out support for the new attributes on window.onerror. Since Errorception already uses window.onerror to record errors, you literally don't need to change a thing (yes, even though the attributes are new). Errorception will record stack traces for your errors whenever available. In fact, I've already tested this with Chrome Canary, and it works like a charm! Yes, this works for Cross-Origin errors too!

This should finally lay to rest the argument about whether try/catch blocks are better for JavaScript error logging, or if one should use window.onerror instead. There's absolutely no advantage to using try/catch blocks for error logging anymore. It's still useful for handling exceptions, sure, but it isn't useful for logging. And if you are using Errorception, you are already using the best mechanism for error logging. Of course, you should be used to that by now. ;)

As always, if you have any questions or feedback, the comments are open.

Monday, 14 October 2013

Say Hello To CORS

Errorception now uses CORS when available to send errors from the browser to the server. This makes the error POSTing process much more lightweight.

This wasn't the case so far. Error information was encoded into form fields and posted into a hidden iframe. While this largely worked fine, it wasn't without its problems. Serialising data as form fields isn't a big deal, even if slightly wordy. However, iframes are very resource-heavy, and are fraught with performance problems. Also, iframes are essentially just regular page loads, so they fire up the browser's loading indicators in the browser tab and show that little message in the status bar of the browser. All of this happens simultaneously. Obviously, users don't like this.

However, two years ago when Errorception was launched, JSON and CORS wasn't as ubiquitous as it is today. Forms and iframes, despite their problems, were the only workable solution, especially if like me you insist on using POST for posting data. Fast-forward to today, and the world is a different place. JSON and CORS are available in every browser worth their salt.

After weeks of development and extensive testing, I've released a new version of the tracking code to make use of JSON and CORS where possible, to ensure that your users see the least amount of performance degradation at any time. If CORS (or Microsoft's XDomainRequest) isn't available, the code falls back to working as it always has - with form fields and iframes.

Upgrade if you haven't already! This new code is only released to people who are using the latest tracking snippet. The new tracking snippet was released earlier this year, and already implements tons of performance improvements over the old one. You should upgrade if you haven't already. If you signed up after May, you are already on the latest code. If you've upgraded already, you rock!