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