Error Handling 101
const ME = Object.freeze({
name: 'Sergey Bolshov',
position: 'Senior Front-end Software Engineer',
workPlace: 'Allegro',
email 'bolszow@gmail.com',
githubUsername: '@bigsergey',
from: 'Kazakhstan'
});
Who am I?
What every programmer must do before he dies?
Break down production
(even once in a professional career)
Yes
The 5th Edition of the Software Fail Watch in 2017 identified:
- impacting 3.7 billion people,
- $1.7 trillion in assets,
- 606 recorded software failures,
- 314 companies.
No
If debugging is the process of removing software bugs, then programming must be the process of putting them in.
Edsger Dijkstra
Source: goodreads.com
Source: TRICENTIS
Are errors bad?
What is an error?
An error (from the Latin error, meaning "wandering")
is an action which is inaccurate or incorrect. In some usages, an error is synonymous with a mistake. In statistics, "error" refers to the difference between the value which has been computed and the correct value. An error could result in failure or in a deviation from the intended performance or behaviour.
Is it GOOD definition?
Error
is constructor which creates an error object.
const error = new Error('Message');
Error
new Error([message[, fileName[, lineNumber]]]);
- three optional parameters
// this:
const x = Error('Error without new');
// has the same functionality as this:
const y = new Error('Error with new');
- used as a function
class PermissionError extends Error {
constructor(message) {
super(message);
this.name = 'PermissionError';
this.message = message;
}
}
- create custom error
Error.prototype.stack
Not part of any specification. Non-standard!!!
Error: OMG, it does not work!
at withErrorObject (index.js:7)
at window.onload (index.js:21)
Error: "OMG, it does not work!"
withErrorObject webpack:///./src/client/index.js?:7
onload webpack:///./src/client/index.js?:21
Chrome 75
Firefox 67
// Syntax error
console.log("hello, world!";
// Syntax error
console.log("hello, world!");
// Runtime error
const user = {
firstName: 'Sergey',
lastName: 'Bolshov',
city: 'Poznań'
};
user.getFullName(); // TypeError: user.getFullName is not a function
// Syntax error
console.log("hello, world!");
// Runtime error
const user = {
firstName: 'Sergey',
lastName: 'Bolshov',
city: 'Poznań'
};
user.getFullName(); // TypeError: user.getFullName is not a function
// Logical error
user.getFullName = function () { return this.city; };
user.getFullName(); // Poznań
Type of errors
Error types
-
RangeError
-
ReferenceError
-
SyntaxError
-
TypeError
-
URIError
-
EvalError
const pi = 3.14159;
pi.toFixed(100000); // RangeError
function foo() {
bar++; // ReferenceError
}
if (foo) { // SyntaxError
// the closing curly brace is missing
var foo = {};
foo.bar(); // TypeError
decodeURIComponent("%"); // URIError
The throw statement
function withoutErrorObject() {
throw "OMG, it does not work!";
}
window.onload = () => withoutErrorObject();
// Uncaught OMG, it does not work!
function withErrorObject() {
throw new Error("OMG, it doea not work!");
}
window.onload = () => withErrorObject();
/**
* Uncaught Error: OMG, it does not work!
* at withErrorObject (index.js:6)
* at onload (index.js:10)
*/
throws a user-defined exception
The throw statement
function withoutErrorObject() {
throw "OMG, it does not work!";
}
window.onload = () => withoutErrorObject();
// Uncaught OMG, it does not work!
function withErrorObject() {
throw new Error("OMG, it does not work!");
}
window.onload = () => withErrorObject();
/**
* Uncaught Error: OMG, it does not work!
* at withErrorObject (index.js:6)
* at onload (index.js:10)
*/
Tip: ESLint rule no-throw-literal
try ... catch ... finally
try ... catch ... finally
try {
// Code to be executed
}
catch (error) {
// Code that are executed if an exception
// is thrown in the try block
}
finally {
// Code that are executed regardless of whether
// an exception was thrown or caught
}
Usage example
function loadData(json) {
let data;
startLoadAnimation();
try {
data = JSON.parse(json); //throws when json has syntax error
if (!data.isValid) {
throw new Error('Data is not valid!');
}
} catch (error) {
showInvalidDataMessage(error);
} finally {
stopLoadAnimation();
}
return data;
}
function loadData(json) {
let data;
startLoadAnimation();
try {
data = JSON.parse(json); //throws when json has syntax error
if (!data.isValid) {
throw new Error('Data is not valid!');
}
} catch (error) {
showInvalidDataMessage(error);
} finally {
stopLoadAnimation();
}
return data;
}
function loadData(json) {
let data;
startLoadAnimation();
try {
data = JSON.parse(json); //throws when json has syntax error
if (!data.isValid) {
throw new Error('Data is not valid!');
}
} catch (error) {
showInvalidDataMessage(error);
} finally {
stopLoadAnimation();
}
return data;
}
function loadData(json) {
let data;
startLoadAnimation();
try {
data = JSON.parse(json); //throws when json has syntax error
if (!data.isValid) {
throw new Error('Data is not valid!');
}
} catch (error) {
showInvalidDataMessage(error);
} finally {
stopLoadAnimation();
}
return data;
}
function loadData(json) {
let data;
startLoadAnimation();
try {
data = JSON.parse(json); //throws when json has syntax error
if (!data.isValid) {
throw new Error('Data is not valid!');
}
} catch (error) {
showInvalidDataMessage(error);
} finally {
stopLoadAnimation();
}
return data;
}
Async error handling
somePromise().then(function () {
throw new Error('oh noes');
}).catch(function (err) {
// I caught your error! :)
});
somePromise().then(function () {
throw new Error('oh noes');
}, function (err) {
// I didn't catch your error! :(
});
Source: We have a problem with promises
Async error handling
async function getData(userId, orderId) {
try {
const user = await getUser(userId);
const order = await getOrder(orderId);
} catch (error) {
// catch errors both in getUser and getOrder
console.error(error);
}
}
Async error handling
function asyncFunction() {
try {
setTimeout(() => {
throw new Error("I'am not gonna be caught!");
}, 1);
} catch (error) {
// it will never enter here
}
}
Async error handling
function asyncFunction() {
setTimeout(() => {
try {
throw new Error("I'am not gonna be caught!");
} catch (error) {
// handle error
}
}, 1);
}
Bad handler
function badHandler(fn) {
try {
return fn();
} catch (e) {
return null;
}
}
- silent fail
- difficult debugging
Ugly handler
function uglyHandler(fn) {
try {
return fn();
} catch (e) {
throw new Error('Function call fails!!!');
}
}
- we know something goes wrong
- the exception gets bubbled through the call stack
- better debugging
- but we lose the original error
Handler with custom error
class SpecifiedError extends Error {
constructor(message) {
super(message);
this.name = 'SpecifiedError';
this.message = message;
}
}
function uglyHandlerImproved(fn) {
try {
return fn();
} catch (e) {
throw new SpecifiedError(e.message);
}
}
Rethrow error
class PermissionError extends Error {
constructor(message) {
super(message);
this.name = 'PermissionError';
this.message = message;
}
}
try {
// assume an exception occurs
} catch (error) {
if (error instanceof PermissionError) {
// Handle PermissionError exceptions
} else {
throw error;
}
}
window.onerror
window.onerror = function (message, source, lineno, columnno, error) {
// ... handle error ...
return false;
}
window.addEventListener('error', function (event) {
// ... handle error ...
const { error, message, filename, colno, lineno } = event;
const { message, stack } = error;
});
“Script error” is what browsers send to the onerror callback when an error originates from a JavaScript file served from a different origin (different domain, port, or protocol).
<script src="http://another-domain.com/app.js" crossorigin="anonymous"></script>
window.onerror
What we know already?
- How to create error
- How to throw error
- How to catch error
How do we know that user spots an error?
JavaScript error reporting/monitoring tools
helps you identify production errors impacting your users' experiences and fix bugs before users report them.
Source: Sentry.io
JavaScript error reporting/monitoring tools
- Sentry [free to $]
- Rollbar [free to $]
- NewRelic [$]
- bugsnag [$]
- errorception [$]
- Honeybadger [$]
- Raygun [$]
- TrackJS [$]
- and others
JavaScript error reporting/monitoring tools
-
Easy to use
- Enhanced stack trace
- Dashboards with:
- Percentage of page views with errors
- Group errors by browser
- Source map support
-
Ignore selected errors
-
...
Initialization is easy
(here and later Sentry as example)
// When using npm, import Sentry
import * as Sentry from '@sentry/browser';
Sentry.init({
dsn: 'https://<key>@sentry.io/<project>'
sampleRate: 0.1, // only 10% of events will be sent
});
npm install @sentry/browser
<script
src="https://browser.sentry-cdn.com/5.4.3/bundle.min.js"
crossorigin="anonymous"
></script>
or
init the Sentry Browser SDK as soon as possible during your page load
Source maps
function withErrorObject() {
throw new Error("OMG, it does not work!");
}
window.onload = () => withErrorObject();
/**
* Uncaught Error: OMG, it does not work!
* at withErrorObject (index.js:6)
* at onload (index.js:10)
*/
!function(e,t){"use strict";!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else{var o=t();for(var n in o)("object"==typeof exports?exports:e)[n]=o[n]}}(t,function(){return function(e){var t={};function o(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,o),r.l=!0,r.exports}return o.m=e,o.c=t,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(e,t){if(1&t&&(e=o(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)o.d(n,r,function(t){return e[t]}.bind(null,r));return n},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="https://assets.allegrostatic.com/opbox-navigation-tiles/",o(o.s=0)}({0:function(e,t,o){e.exports=o("O14P")},O14P:function(e,o){t.onload=function(){return function(){throw Error("OMG, it does not work!")}()}}})})}(0,"undefined"!=typeof window?window:global);
/**
* Uncaught Error: OMG, it does not work!
* at index_ecb63409.js:1
* at O14P.t.onload (index_ecb63409.js:1)
*/
Source maps using Webpack
npm install --save-dev @sentry/webpack-plugin
// webpack.config.js
const SentryWebpackPlugin = require('@sentry/webpack-plugin');
module.exports = {
// other configuration
plugins: [
new SentryWebpackPlugin({
include: '.',
ignoreFile: '.sentrycliignore',
ignore: ['node_modules', 'webpack.config.js'],
configFile: 'sentry.properties'
})
]
};
sentry-cli releases files VERSION upload-sourcemaps /path/to/sourcemaps
Ignoring errors from third-party scripts and unsupported browsers
Sentry.init({
dsn: 'https://<key>@sentry.io/<project>',
whitelistUrls: [
'www.example.com/static/js', // your code
'ajax.googleapis.com' // code served from Google CDN
],
});
- Legacy browsers
- Web crawlers
- Browser extensions
Nice to read: Tips for Reducing JavaScript Error Noise
Use inbound data filters:
Error agregation issues
- Different browsers
- Different localizations
// Error IE: Obiekt nie obsługuje właściwości lub metody „values”
// Error Safari: Object.values is not a function.(In 'Object.values(r)', 'Object.values' is undefined)
// Error Chrome: Object.values is not a function
Enriching error data
Anything that can go wrong will go wrong
Murphy's law
Thank YOU!
Resources:
- GIF's from GIPHY
- https://www.stacktracejs.com/
- Exceptional Exception Handling in JavaScript
- A Guide to Proper Error Handling in JavaScript
- Proper error handling in JavaScript
- The Cost of Poor Quality Software in the US: A 2018 Report
- Custom JavaScript Errors in ES6
- Capture and report JavaScript errors with window.onerror
- What the heck is "Script error"?
- Error Handling in JavaScript
- We have a problem with promises
What does "101" mean?
It means "introductory something".
The allusion is to a college course with the course code 101, which in the American system and probably others indicates an introductory course, often with no prerequisites.
Source: english.stackexchange.com
Error Handling 101
By Sergey Bolshov
Error Handling 101
- 1,185