As part of building the FlexePark app, my team invested a lot of energy in capturing every conceivable exception and error, and making sure the error got logged back to our service layer with as much context as possible. We even added exception tracking via Google Analytics for good measure. But researching errors was still tedious, and even determining which errors demanded attention was sometimes challenging.
So when I was at the Kansas City Developer Conference this summer I spent a lot of time talking with Todd H. Gardner and the folks with TrackJS. The ability to view client-side issues with 100% of that sweet, sweet user context had me sold. So while I was still at the conference I added a story to our Jira board: “Add TrackJS for error reporting”.
Yesterday was the lucky day when I got to snag that card! It all started with this tweet:
I just registered for @trackjs and am working on getting this added to @flexepark to improve our error tracking. Really looking forward to seeing how this helps us improve our customer experience.
— Michael Dowden (@mrdowden) August 17, 2018
After signing up for TrackJS I was taken straight to the installation instructions, which look like this:
<!-- BEGIN TRACKJS -->
<script type="text/javascript">window._trackJs = { token: 'YOUR_TOKEN_HERE' }</script>
<script src="https://cdn.trackjs.com/releases/current/tracker.js"></script>
<!-- END TRACKJS -->
This is great for a POC and even some production web apps. But a lot of Angular 5+ apps (like ours) prefer to install libraries via NPM and avoid doing any setup and configuration directly within the index.html
file. As I started down this road I realized that while there is a ton of great documentation on Build and Framework Integrations, there isn’t really anything specific to a full Angular Typescript integration.
After muddling through for a bit I learned a lot about how an Angular app loads and came up with an integration that seems to work well. This is what I have documented below.
[UPDATE: 8-MAY-2019 — using new module-based installation]
It’s pretty easy to find the trackjs NPM package. Installation is simply:
$ npm install trackjs --save
Configuration took a bit longer. I started by adding a basic configuration to my environment.ts
file:
export const environment = {
TrackJS: {
token: '<get your own, thanks>',
enabled: true,
application: 'flexepark',
console: { display: true },
},
};
TrackJS supports multiple applications, so if you want to have multiple apps use your TrackJS account you can totally do this by setting up applications. (Our implementation is for FlexePark, as previously mentioned.) You can also use multiple application to support various environments (for example, we could use ‘flexepark-dev’).
Note that I’ve gone ahead and explicitly included both the enabled
and console.display
values since I’ll probably want these to change across environments (especially since I’m not using multiple applications right now).
Next up I need to be sure that TrackJS is loaded as early as possible when my Angular app is starting up. This is the part I couldn’t find documented anywhere and I honestly wasn’t sure where to start. Turns out, it’s pretty simple — add the necessary setup bits to your main.ts
file:
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';import { TrackJS } from 'trackjs';
// Initialize TrackJS for error handling
TrackJS.install(environment.TrackJS);
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.log(err));
Note that we’ve pulled the installation configuration directly from the current environment configuration.
Believe it or not, that’s it! You now have TrackJS integrated into your Angular startup process. Everything is working!! (See the Quick Start guide for instructions on verifying this.)
But, we want to do a bit more …
One thing I really wanted to do is take advantage of the app version attribute that TrackJS supports. I also didn’t want to add yet another thing I need to remember to maintain manually, so I figured the version from the package.json
file would work best.
We can add this directly into the configuration within the TrackJS section of our environment.ts
file:
TrackJS: {
version: require('../../package.json').version,
}
Unfortunately, if you try to do a build now you’ll get an error:
error TS2304: Cannot find name ‘require'
To solve this we need to do two things:
- Edit your
src/tsconfig.app.json
file to include"types": ["node"]
- Add
@types/node
to your dependencies. If it’s already in yourpackage.json
file underdevDependencies
you can simply move this entry to thedependencies
section. Or if it’s not in the file at all you can install it with:$ npm install @types/node --save
Now when you run your build everything should work as expected, and performing a test on TrackJS should reveal that your app version is now being reported!
[UPDATE 8-MAY-2018] Everything is working now, but there’s a small problem. If you want to completely disable TrackJS (for example, in your local development environment) you’ll get an error when removing the environment variable. Left as-is you’ll discover a small error: “TrackJS could not find a token”.
This is a simple update to the main.ts
file:
// Initialize TrackJS for error handling
TrackJS.install(environment.TrackJS);
becomes:
// Initialize TrackJS for error handling
if (environment.trackJs) { TrackJS.install(environment.TrackJS); }
This defensive configuration block simply avoids using TrackJS altogether if it’s not configured for a given environment.
There’s just one more big step in making sure you have a full Angular 5+ integration and that is setting up an ErrorHandler
. This is well covered in the TrackJS framework integrations, but I added a little something special I wanted to share, which is logging the exception to Google Analytics.
I followed the TrackJS instructions, but since we already had an ErrorHandler called GlobalErrorHandler
I added the necessary configuration here:
import { Injectable, ErrorHandler, Injector } from '@angular/core';
import { Angulartics2 } from 'angulartics2';
import { TrackJS } from 'trackjs';
@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
constructor(public injector: Injector) { }
handleError(error) {
// This can have useful content for TrackJS (and our developers)
console.warn(error.message);
// Send the native error object to TrackJS
TrackJS.track(error.originalError || error);
// Track the event in Google Analytics
const analytics = this.injector.get(Angulartics2);
analytics.exceptionTrack.next({ fatal: true, description: error.message });
}
}
The above error handler assume that you’re using Angulartics2 for your analytics integration, but you can easily substitute your own integrations here.
The important bit is to be sure you use the injector
to pull in external services instead of the constructor injection more commonly used by Angular services.
The only additional thing I did is add a post-load configuration to setup User tracking. You need to make the following call wherever you get notified about user login/logout actions:
TrackJS.configure({ userId: user.id });
And that’s it! You now have a TrackJS implementation in Angular 5+ using a more “angular-y” implementation, and you are even tracking errors by application version! My initial implementation took 3 hours and was celebrated in this tweet:
I now have @trackjs all 100% fully integrated on @flexepark , with an amazing global Error Handler and big, beautiful error logs that provide actionable intel. Complete with app version # and User ID. :)
— Michael Dowden (@mrdowden) August 17, 2018
Please let me know if you have any suggestions or corrections to anything you try here.
Good luck bug hunting.