KEMBAR78
Lightning Talk: JavaScript Error Handling | PPTX
JavaScript Error Handling
Nick Burwell
Principal UX Engineer
Invoca, Inc.
Why are JavaScript errors so scary?
Why are JavaScript errors so prominent?
Why are JavaScript errors so often ignored?
A few thoughts...
Dynamic language, weakly-typed
Intersection of JS and HTML/DOM
Browser differences
Often lack of test coverage
Assets missing / order dependencies
Spotty networks, load errors
Do your developers & QA notice
errors locally?
How easy are they to debug
once they occur?
Do you know about errors that
happen in production?
So how do we do better?
CC: https://www.flickr.com/photos/verpletterend/5393470920
Do your developers & QA notice errors locally?
Components should enforce their contract
Raise errors when not met
Don't just silently fail
Provide useful context to debug
Example from DataTables
Example from React components
React = require('react');
var Button = React.createClass({
propTypes: {
title: React.PropTypes.string.isRequired
},
...
});
Basic Error Handling
function foo() {
if (typeof title === "undefined") {
throw new Error("title should be defined");
}
}
try {
foo();
} catch (ex){
// handle error
}
Error Hierarchy
Error
EvalError RangeError ReferenceError SyntaxError TypeError URIError
Documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
Define your own Error
function NotFoundError(message){
this.message = message;
}
NotFoundError.prototype = new Error();
...
throw new NotFoundError("Could not find ID: foobar");
source
Surprising results!
No stack, no error name
Define your own Error
function NotFoundError(message){
this.name = "NotFoundError";
this.message = message;
this.stack = (new Error()).stack;
}
NotFoundError.prototype = Object.create(Error.prototype);
NotFoundError.prototype.constructor = NotFoundError;
...
throw new NotFoundError("Could not find ID: foobar");
< 8
Have an easy way to log errors
Simple interface
Adapt based on whether in dev or production
Show error prominently in dev!
Send to exception monitoring in production
Don't catch simply to log to console
Don't simply log to console as the way to "handle" errors
try {
// something that causes an error
} catch (ex){
console.log("ERROR: " + ex.message + "n" + ex.stack);
}
Antipattern!
Expect the unexpected
var status = this.model.get('status');
switch (status) {
case 'active':
// do something here...
break;
case 'paused':
// do something else here...
break;
case 'archived':
// do different stuff here...
break;
default:
Invoca.logError("No status handling for value: " + status, context);
}
Catch and report ALL the errors!
window.onerror = function(
message,
scriptUrl,
lineNumber,
characterNumber,
error) {
// handle error!
Notifier.log(message, error.stack);
// return true; prevents default browser handling
}
Now supported by all
modern browsers (even IE!)
… except Safari
Before...
You couldn't get a stack from window.onerror errors
Two common strategies to work around were:
Rely on onerror anyway
In production mode have a much harder problem debugging issues
Put try/catch's around all your code
Difficult to ensure around all event handlers, timers, ajax callbacks
If exception is caught in development, harder to use browser tools to debug
Middle ground: dynamically wrap with try/catch in production only
Now...
Use window.onerror
Consider also using a library to wrap events / callbacks
In production, log errors to your error monitoring server
Invoca combines JS errors with our Rails / other ruby errors
Includes user / session / browser / URL data
3rd party solutions available
Bugsnag
Rollbar
In development, let unexpected errors bubble up / rely on browser tools
Minification & obfuscation
Even a message & stack doesn't mean you can decipher uncaught errors
Solution: allow your sourcemap files to be accessible
developers can unobfuscate and make sense of errors
Ensure sourcemaps are enabled in your minifying / concatenating of assets
Rails asset pipeline doesn't provide out of box (fix with precompile task)
Node based options usually have config options for sourcemaps
A full development cycle strategy
Make components easy to integrate with in development
Make things easy to debug in development & testing (when errors happen)
Report unexpected errors to a logging / monitoring system in production
Don't expose ugly exceptions to users in production
Resources
Pre-built logging & monitoring tools
Bugsnag: Product | JS logging repo
Rollbar: Product | JS logging repo
New Relic: Product
Log4JavaScript: Docs
Articles on JS errors
https://bugsnag.com/blog/js-stacktraces
https://www.nczonline.net/blog/2009/03/10/the-art-of-throwing-javascript-errors-part-2/
https://www.nczonline.net/blog/2009/04/28/javascript-error-handling-anti-pattern/
Thanks!

Lightning Talk: JavaScript Error Handling

  • 1.
    JavaScript Error Handling NickBurwell Principal UX Engineer Invoca, Inc.
  • 2.
    Why are JavaScripterrors so scary?
  • 3.
    Why are JavaScripterrors so prominent?
  • 4.
    Why are JavaScripterrors so often ignored?
  • 5.
    A few thoughts... Dynamiclanguage, weakly-typed Intersection of JS and HTML/DOM Browser differences Often lack of test coverage Assets missing / order dependencies Spotty networks, load errors
  • 6.
    Do your developers& QA notice errors locally? How easy are they to debug once they occur? Do you know about errors that happen in production?
  • 7.
    So how dowe do better? CC: https://www.flickr.com/photos/verpletterend/5393470920
  • 8.
    Do your developers& QA notice errors locally? Components should enforce their contract Raise errors when not met Don't just silently fail Provide useful context to debug
  • 9.
  • 10.
    Example from Reactcomponents React = require('react'); var Button = React.createClass({ propTypes: { title: React.PropTypes.string.isRequired }, ... });
  • 11.
    Basic Error Handling functionfoo() { if (typeof title === "undefined") { throw new Error("title should be defined"); } } try { foo(); } catch (ex){ // handle error }
  • 12.
    Error Hierarchy Error EvalError RangeErrorReferenceError SyntaxError TypeError URIError Documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
  • 13.
    Define your ownError function NotFoundError(message){ this.message = message; } NotFoundError.prototype = new Error(); ... throw new NotFoundError("Could not find ID: foobar"); source Surprising results! No stack, no error name
  • 14.
    Define your ownError function NotFoundError(message){ this.name = "NotFoundError"; this.message = message; this.stack = (new Error()).stack; } NotFoundError.prototype = Object.create(Error.prototype); NotFoundError.prototype.constructor = NotFoundError; ... throw new NotFoundError("Could not find ID: foobar"); < 8
  • 15.
    Have an easyway to log errors Simple interface Adapt based on whether in dev or production Show error prominently in dev! Send to exception monitoring in production
  • 16.
    Don't catch simplyto log to console Don't simply log to console as the way to "handle" errors try { // something that causes an error } catch (ex){ console.log("ERROR: " + ex.message + "n" + ex.stack); } Antipattern!
  • 19.
    Expect the unexpected varstatus = this.model.get('status'); switch (status) { case 'active': // do something here... break; case 'paused': // do something else here... break; case 'archived': // do different stuff here... break; default: Invoca.logError("No status handling for value: " + status, context); }
  • 20.
    Catch and reportALL the errors! window.onerror = function( message, scriptUrl, lineNumber, characterNumber, error) { // handle error! Notifier.log(message, error.stack); // return true; prevents default browser handling } Now supported by all modern browsers (even IE!) … except Safari
  • 21.
    Before... You couldn't geta stack from window.onerror errors Two common strategies to work around were: Rely on onerror anyway In production mode have a much harder problem debugging issues Put try/catch's around all your code Difficult to ensure around all event handlers, timers, ajax callbacks If exception is caught in development, harder to use browser tools to debug Middle ground: dynamically wrap with try/catch in production only
  • 22.
    Now... Use window.onerror Consider alsousing a library to wrap events / callbacks In production, log errors to your error monitoring server Invoca combines JS errors with our Rails / other ruby errors Includes user / session / browser / URL data 3rd party solutions available Bugsnag Rollbar In development, let unexpected errors bubble up / rely on browser tools
  • 23.
    Minification & obfuscation Evena message & stack doesn't mean you can decipher uncaught errors Solution: allow your sourcemap files to be accessible developers can unobfuscate and make sense of errors Ensure sourcemaps are enabled in your minifying / concatenating of assets Rails asset pipeline doesn't provide out of box (fix with precompile task) Node based options usually have config options for sourcemaps
  • 24.
    A full developmentcycle strategy Make components easy to integrate with in development Make things easy to debug in development & testing (when errors happen) Report unexpected errors to a logging / monitoring system in production Don't expose ugly exceptions to users in production
  • 25.
    Resources Pre-built logging &monitoring tools Bugsnag: Product | JS logging repo Rollbar: Product | JS logging repo New Relic: Product Log4JavaScript: Docs Articles on JS errors https://bugsnag.com/blog/js-stacktraces https://www.nczonline.net/blog/2009/03/10/the-art-of-throwing-javascript-errors-part-2/ https://www.nczonline.net/blog/2009/04/28/javascript-error-handling-anti-pattern/ Thanks!

Editor's Notes

  • #6 A programming environment where you don't always control all the code that exists on the page, nor when it is available Other libraries can come in and add/change variables, objects, DOM elements
  • #7 We've come a long way from the days of IE6 script errors! But there are still lots of errors. With all the environmental (network, browser, plugins etc) issues, we should be MORE concerned, not less, about these errors. I want to cover the above bullets in this talk and hopefully give you all some ideas on ways to improve your JS code and ability to debug errors.
  • #11 In contrast to the above, here is React components This is good, but not great: An error in console, easy to miss (doesn't break the component, doesn't show anything to developer) In production - doesn't even log!
  • #14 Defining your own errors can be good, just don't do it like this! (animate) You will find this example when searching online, so be careful
  • #15 Here is the proper way to do it. Provides extra context implicitly, can search for the errors in code base easier; could be namespaced based on component or sub system Custom errors are not supported in < IE8 (at least when uncaught, the message becomes "exception thrown but not caught")
  • #17 Browsers will already log on errors and more Pause on exceptions View interactive call stack and local vars Prevents the global onerror hook from firing
  • #18 What we do at Invoca: In development & staging, show the error prominently, including the stack trace
  • #19 But we also don't catch the error or prevent default behavior, so you can still click "pause on exceptions" Use the browser to debug the issue
  • #20 True story from just last week where a situation just like this started logging errors in production Easy to identify the issue, was fixed the same day
  • #21 (animate) May want to return true in production (to avoid errors showing up for users)
  • #23 Tools are great, but you want some structure around when and how you will be sending errors The onerror event is a safety blanket, but much better context can be achieved with focused error handling
  • #24 Just want to quickly touch on minified & obfuscated code...
  • #25 In summary… in dev: easy to log, have that log contain as much context as you can in production: degrade gracefully
  • #26 Resources for those that get the slides later