% Preamble %
I am setting up ember-cli-sentry and am looking to identify the user so that when we get exceptions reported, we know what user those exceptions were associated with, in case they were user-specific. (Say, we deploy to staging, we got some data from the backend that was bad, causing certain errors with the only the users who got that bad data into their account).
& Actual question &
I want to execute some code immediately after the user model loads. I do not know what route is going to be triggering the user model to load.
In theory, actually, the earliest possible place I could get the user model data is immediately after the request for that data is completed. I wonder, would it be crazy to hijack the XHR method, and when the user id is requested, run my code to tell raven/sentry who this user is?
It would be better if there was simply a "loaded" hook for model files, but I've never heard of such a thing. Maybe this is the one use case where this would be desirable?
An h1 for google/seo:
Thank you @duizendnegen and @Isaac Askew, observers were a great starting point and led me to the final solution. This observer code, placed in my application route, helped me figure out exactly what I could add as user context:
window.serviceState = {};
// eslint-disable-next-line
let serviceState = window.serviceState;
export default Route.extend(ApplicationRouteMixin, {
// observes services for state changes we care about and stores them in
// a separate variable so when an exception occurs, we can simply include that
// variable and avoid the risk of causing another exception by trying
// to read them in `captureException`
serviceStateTracker: observer(
'service1.prop1',
'service2.propA',
function() {
[
'service1.prop1',
'service2.propA',
].forEach((prop) => {
let newValue = this.get(prop);
if (serviceState[prop] !== newValue) {
if (serviceState[prop] === undefined) {
console.log(prop, newValue);
} else {
console.log(prop, 'changed from:', serviceState[prop], 'to:', newValue);
}
serviceState[prop] = newValue;
}
});
}
),
However, I did try just get
ing the props I care about in my logger
service at exception time instead, and this worked well. It's better because you won't have code needlessly executing every time a property you want to log changes:
// app/services/logr.js
// initially generated with ember-cli-sentry's `ember g logger logr`
import RavenLogger from 'ember-cli-deploy-sentry/services/raven';
import Ember from 'ember';
const { Logger, inject: { service } } = Ember;
export default RavenLogger.extend({
unhandledPromiseErrorMessage: '',
user: service(),
i18n: service(),
addContext() {
if (this == null) {
// eslint-disable-next-line
console.warn('logr cannot addContext because this is null/undefined, continuing without');
return;
}
let tagsContext = {
locale: this.get('i18n.locale'),
id: this.get('user.id'),
firstName: this.get('user.firstName'),
lastName: this.get('user.lastName'),
email: this.get('user.email')
};
// console.log('tagsContext', tagsContext);
// For some reason setUserContext was not working, but setTagsContext was,
// so I am using tags for all the user info. Maybe upgrading raven could fix this?
this.callRaven('setTagsContext', tagsContext);
// why use callRaven: https://github.com/damiencaselli/ember-cli-sentry/issues/58
},
// work around for unmerged PR:
// https://github.com/damiencaselli/ember-cli-sentry/pull/97
captureException(/* error */) {
if (this.get('isRavenUsable')) {
this.addContext();
window.Raven.captureException(...arguments);
} else {
Logger.debug(...arguments);
}
return true;
},
captureMessage(/* message */) {
if (this.get('isRavenUsable')) {
this.addContext();
window.Raven.captureMessage(...arguments);
} else {
Logger.debug(...arguments);
}
return true;
}
});
For some reason the observer wouldn't work in my logr service..
config/DEPLOY.js
, not config/environment.js
To trigger an error somewhere in some application route hook:
setTimeout(() => {
// I was having some issue with `throw new Error()` so I did this instead
// It's actually just a better way to manually 'throw an exception'
// because it will not forcibly crash the application, but instead
// at least attempt to continue running
this.get('logr').captureException(new Error('sentry user context test'));
}, 10000);
development: false
to let raven actually send this test error to sentry (unless you aren't testing end to end) (I typically have this set: development: environment === 'development'
In application route actions, add this to your error action handler:
error(error) {
// Trap unhandled failures during routing:
// Should maybe be handled by the sentry addon, but is not as of now:
// https://github.com/damiencaselli/ember-cli-sentry/issues/105
this.get('logr').captureException(error);