anchorember-cli-sentry
The ember-cli-sentry
addon has been around since 2015 and nicely integrates the raven-js
library with Ember.js. What is raven-js
? It is the official browser JavaScript client for Sentry.
Well... it was the official browser JavaScript client for Sentry. In 2018 it has been replaced by @sentry/browser
with an entirely new API.
What does that mean for ember-cli-sentry
? We don't know! We have been experimenting with @sentry/browser
though and we are starting to think that a dedicated Ember.js addon for Sentry might not be needed anymore. In the rest of this post we will show you how we integrated @sentry/browser
into one of our apps and how to migrate from ember-cli-sentry
to @sentry/browser
.
anchorSourcemaps
TypeError: Cannot read property 'name' of undefined
at Object.ie [as get](/assets/vendor-394212fdd48a8a8e9508401b5be54d75.js:1347:16)
at ? (/assets/vendor-394212fdd48a8a8e9508401b5be54d75.js:9923:46)
at e(/assets/vendor-394212fdd48a8a8e9508401b5be54d75.js:9947:46)
at n.filter(/assets/vendor-394212fdd48a8a8e9508401b5be54d75.js:9827:26)
at n._performFilter(/assets/vendor-394212fdd48a8a8e9508401b5be54d75.js:9834:196)
at n.search(/assets/vendor-394212fdd48a8a8e9508401b5be54d75.js:9793:162)
Sourcemaps allow you to map from compiled and minified code to the original code you wrote in your editor. That makes it a lot easier to decipher stack traces like the one above because it will show you the actual file and function names.
By default in Ember.js, sourcemaps are disabled for production builds. In this case we would like to have them though, so that Sentry has access to them and can decipher the cryptic stack traces for us. We can enable sourcemap generation for all environments in the ember-cli-build.js
file:
let app = new EmberApp(defaults, {
sourcemaps: {
enabled: true,
},
});
If you now run ember build --prod
it should generate .map
files next to the JavaScript files in the dist/assets/
folder. Make sure to upload those files to your server too, so that Sentry can access them.
In case you don't want other people to be able to read your original source code you can also upload the sourcemaps directly to Sentry. If you use ember-cli-deploy
to publish your apps then you can use the ember-cli-deploy-sentry
addon to do this automatically for each deployment.
anchorSetting up @sentry/browser
ember-cli-sentry
uses an instance-initializer to automatically configure and initialize the raven-js
library. Those initializers are great for certain cases, but it could happen that a bug in a different initializer is triggered before the Sentry client was setup to listen for errors. For this reason we will not use an initializer to setup @sentry/browser
.
Instead, we will adjust our app/app.js
file to initialize @sentry/browser
right before we start the app:
import { startSentry } from './sentry';
startSentry();
const App = Application.extend({
// ...
});
From the above snippet you can see that we chose to put the Sentry-specific logic into a separate file: app/sentry.js
. That file looks roughly like this:
import * as Sentry from '@sentry/browser';
import { Ember } from '@sentry/integrations/esm/ember';
import config from './config/environment';
export function startSentry() {
Sentry.init({
...config.sentry,
integrations: [new Ember()],
});
}
First we import the @sentry/browser
library into the file using a wildcard import and we import their official Ember.js plugin from the @sentry/integrations
package:
import * as Sentry from '@sentry/browser';
import { Ember } from '@sentry/integrations/esm/ember';
We are using @sentry/integrations/esm/ember
here instead of just @sentry/integrations
because we want to make sure that we only bundle the Ember.js plugin, but not e.g. the Vue plugin too.
At this point you might be wondering: but, we never installed those libraries?! And you are totally right! At this point we need to install the libraries using your favorite JavaScript package manager. In this case we'll use yarn
:
yarn add @sentry/browser @sentry/integrations
To make those available in the app we'll also need ember-auto-import
, which makes importing from node_modules
much easier:
yarn add --dev ember-auto-import
Now we have everything we need installed and can go on with the snippet above. You can see that the Sentry.init()
call uses the sentry
object from your application configuration, so let's add that to the config/environment.js
file:
module.exports = function(environment) {
let ENV = {
sentry: {
environment,
}
};
if (environment === 'production') {
ENV.sentry.dsn = 'https://<key>@sentry.io/<project>';
}
return ENV;
}
... and we're done! @sentry/browser
is now sufficiently set up and will report any errors on the page to the server configured in the dsn
property above.
anchorFiltering Errors
Some errors we just don't care about. One example of that is the TransitionAborted
error that the Ember.js router reports when a route transition was cancelled. We can ignore such errors by implementing the beforeSend()
hook of @sentry/browser
:
Sentry.init({
// ...
beforeSend(event, hint) {
let error = hint.originalException;
// ignore aborted route transitions from the Ember.js router
if (error && error.name === 'TransitionAborted') {
return null;
}
return event;
},
});
anchorManually Reporting Errors
With ember-cli-sentry
you could use the raven
service to manually report messages or exceptions to Sentry. With @sentry/browser
you can do the same:
import * as Sentry from '@sentry/browser';
Sentry.captureMessage('Something is broken! 😱');
Sentry.captureException(new Error('with stack trace!! ✨'));
anchorAdding Additional Context
Sentry, by default, records the IP of the user that has experienced the error. But when your app has user accounts and an authentication system, it is much more useful to know which specific user this has happened to. @sentry/browser
allows us to add additional context to the events it sends to the server.
We could do that manually in the beforeSend()
hook, but it is much easier to use the configureScope()
function for this:
Sentry.configureScope(scope => {
scope.setUser({
id: 42,
email: "john.doe@example.com"
});
});
This will automatically add the user information for all errors/events that follow after this call. If you only want to add such information for a single event you can use the withScope()
function instead.
anchorTesting
We could mock the raven
service of ember-cli-sentry
in tests like this:
this.owner.register('service:raven', Service.extend({
captureException(error) {
// ...
}
}));
But with @sentry/browser
this becomes a little more complicated since there is no service anymore that could be mocked like that. We have experimented with this and found that pushing an additional scope on the stack and configuring a mock client for that scope seems to result in a useful way to assert whether an exception was correctly reported.
The details of this require its own blog post though, since they are a little less straight-forward than what we described above.
If you have questions, want to know more or need help setting all of this up for your apps please contact us. We're happy to help!