The frontend is
reliable
not
Maurizio @sithmel
A talk about frontend monitoring
Introducing Airbrake
Airbrake service

Airbrake service

Official airbrake client
https://github.com/airbrake/airbrake-js
var airbrake = new airbrakeJs.Client({
projectId: 12345,
projectKey: 'xxxxxxxxxxxx',
});
try {
// it throws if the document has no head tag
const style = document.createElement('style');
document.head.insertBefore(style);
} catch(err) {
airbrake.notify(err);
throw err;
}
Official airbrake client
- Captures automatically uncaught errors
you have to filter third party js errors and sensitive info
- Reconstructs history that led to the error
very invasive!
- It weights down your bundle
A LOT
airbrake-mini-client
https://github.com/tes/airbrake-mini-client
var airbrake = airbrakeMini({
projectId: 12345,
projectKey: 'xxxxxxxxxxxx',
});
try {
// if throws if the document has no head tag
const style = document.createElement('style');
document.head.insertBefore(style);
} catch(err) {
airbrake.notify(err);
throw err;
}
airbrake-mini-client
Same basic API
But no extra features
Lightweight size
airbrake-mini-client
Suggested config:
const airbrake = airbrakeMini({
projectId: 12345,
projectKey: 'xxxxxxxxxxxxxxx',
environment: getEnvironment(window.location.hostname)
})
airbrake-mini-client
Suggested config:
airbrake.addFilter((notice) => {
const { context } = notice;
if (!context) return null;
// filters out saved pages
if (context.url && context.url.indexOf('file') === 0) {
return null;
}
// filters out translation services and local env
if (context.environment === 'unknown' ||
context.environment === 'local') {
return null;
}
return notice;
});
airbrake-mini-client
getEnvironment:
const envs = [
{ regexp: /\.tes\.com$/, name: 'production' },
{ regexp: /\.tes-dev\.com$/, name: 'development' },
{ regexp: /\.tes-stage\.com$/, name: 'staging' },
{ regexp: /\.tes-local\.com$/, name: 'local' }];
function getEnvironment (hostname) {
// hostname is window.location.hostname
for (let i = 0; i < envs.length; i++) {
const { regexp, name } = envs[i];
if (regexp.test(hostname)) return name;
}
return 'unknown';
}
Use React error boundary
class ErrorBoundary extends Component {
state = { hasError: false };
componentDidCatch (error, info) {
this.setState({ hasError: true });
this.props.onError(error, info);
}
render () {
if (this.state.hasError) return <div>Try later</div>;
return this.props.children;
}
}
function getEBWrapper ({ onError }) {
return (Component) => (props) => (
<ErrorBoundary onError={onError}>
<Component {...props} />
</ErrorBoundary>);
}
Use React error boundary
const onError = (error, info) =>
airbrake.notify({ error, params: { info })
const errorBoundaryWrapper = getEBWrapper({ onError });
render(errorBoundaryWrapper(App), mountPoint);
What can possibly go wrong?
What can possibly go wrong?

Error messages are localised and change across browsers
What can possibly go wrong?
Normalise exceptions:
try {
response = await fetch(endpoint, options);
} catch (e) {
// normalises error messages
throw new Error('endpoint fetch Error');
}
What can possibly go wrong?

Network errors :-(
What can possibly go wrong?
Defer network request until on line
import deferUntilOnline from 'defer-until-online';
const deferUntil = deferUntilOnline({ timeout: 10000 });
const myfunc = deferUntil(() => {...});
const result = await myfunc(...);
What can possibly go wrong?
retry (if you can)
import retry from 'async-deco/retry';
const retryDecorator = retry({ times: 10, interval: 1000 });
const myfunc = retryDecorator(() => {...})
What can possibly go wrong?

What can possibly go wrong?
browser support issues
This doesn't replace browser testing!!!
Syntax errors will not be caught!!!
(missing transpilation for example)
What can possibly go wrong?
Try capture compatibility issues with es-check!
https://www.npmjs.com/package/es-check
{
...
"scripts": {
...
"build": "webpack && es-check es5 dist/js/*.js"
}
}
Enjoy monitoring your app!
Maurizio @sithmel
The frontend is reliable
By Maurizio Lupo
The frontend is reliable
- 550