Integrate Rollbar with Aurelia

Posted on 2017-08-19 in Aurelia

I recently integrated Rollbar (a service to Detect, diagnose and debug errors with ease) into an Aurelia application. I explain here how I did it. I start by explaining how logging works with Aurelia and then how I integrated Rollbar into this.

Logging with Aurelia

The main module to log things with Aurelia is simply named aurelia-logging. It is responsible for:

  • Setting the log level you want with setLevel.
  • Create/get loggers with getLogger. When you get a logger with this function, logs messages will be dispatched to all registered log appenders (more on that in a minute) while respecting the level of log you set.
  • Defining the Appender interface.

So the aurelia-logging is a generic module to deal with logging but doesn't log anything by itself. It is defined this way so it can work in various environment (eg the browser and Node) and send logs where you want (let it be the browser console or Rollbar).

To actually do some logging, we need to add at least one Appender with addAppender. For instance, you can use the aurelia-logging-console module to append log to the browser console. Or you can use au-rollbar to log the messages into Rollbar.

Let's illustrate this with an example. Let's say I configure the log level to log only warnings and above with Logger.setLevel(Logger.logLevel.warn); and that you added the ConsoleAppender and RollbarAppender to the list of appenders with Logger.addAppender(new ConsoleAppender()) and Logger.addAppender(new RollbarAppender());.

You then create your logger with let myLogger = Logger.getLogger('my-module'). You then use the logger, eg:

myLogger.warn('A warning')
myLogger.debug('A debug message')

The A warning message will be dispatched to the console and rollbar while A debug message won't be logged anywhere.

Integration with Rollbar

Using Rollbar within Aurelia

In order to integrate Rollbar into Aurelia, I created a RollbarAppender that implements the Appender interface (see the interface definition here). What it does is pretty straightforward: it dispatches incoming log messages to the proper methods of the Rollbar object. Just like in ConsoleAppender I prepend the level and id of the logger to the message.

Since according to the doc I can only give one message, I put the eventual extra parameters of the function in a JSON object to log them as extra arguments.

Here is the full code. You can download it here. You can also install it with npm install au-rollbar.

 1 export default class RollbarAppender {
 2     debug(logger, ...rest) {
 3         let mainArgs = rest[0];
 4         this.getRollbar().debug(`DEBUG [${logger.id}]: ${mainArgs}`, this._formatRest(rest));
 5     }
 6 
 7     info(logger, ...rest) {
 8         let mainArgs = rest[0];
 9         this.getRollbar().info(`INFO [${logger.id}]  ${mainArgs}`, this._formatRest(rest));
10     }
11 
12     warn(logger, ...rest) {
13         let mainArgs = rest[0];
14         this.getRollbar().warning(`WARN [${logger.id}] ${mainArgs}`, this._formatRest(rest));
15     }
16 
17     error(logger, ...rest) {
18         let mainArgs = rest[0];
19         this.getRollbar().error(`ERROR [${logger.id}]  ${mainArgs}`, this._formatRest(rest));
20     }
21 
22     _formatRest(rest) {
23         try {
24             return JSON.stingify({ extra: rest.slice(1) });
25         } catch (e) {
26             return undefined;
27         }
28     }
29 
30     getRollbar() {
31         if (!window.Rollbar) {
32             if (console.warn) {  // eslint-disable-line no-console
33                 console.warn('Rollbar is not defined');  // eslint-disable-line no-console
34             }
35 
36             return {
37                 debug: () => {},
38                 info: () => {},
39                 warning: () => {},
40                 error: () => {},
41             };
42         }
43 
44         return window.Rollbar;
45     }
46 }

You then need to import it with something like import RollbarAppender from './logging'; (or import RollbarAppender from 'au-rollbar' if you installed it with npm). You then need to configure your logging. Since I doubt you will want to enable it in development, I suggest you configure Aurelia like this (in your main.js file):

 1 import RollbarAppender from 'au-rollbar'
 2 import * as Logger from 'aurelia-logging';
 3 
 4 export function configure(aurelia) {
 5     aurelia.use.standardConfiguration();
 6 
 7     if (environment.debug) {
 8         // Only log to the console when developing (default configuration of developmentLogging)
 9         aurelia.use.developmentLogging();
10     } else {
11         // Production logging is inspired by developmentLogging
12         // This loads the ``aurelia-logging-console`` module and then configures logging.
13         aurelia.use.preTask(() => {
14             return aurelia.loader.normalize(
15                     'aurelia-logging-console',
16                     aurelia.bootstrapperName
17             ).then(name => {
18                 return aurelia.loader.loadModule(name).then(m => {
19                     // Once the module is loaded, we configure logging.
20                     // We add the ConsoleAppender so all messages are logged in the console of the browser.
21                     Logger.addAppender(new m.ConsoleAppender());
22                     // If the Rollbar exists in the window object (ie if Rollbar is loaded),
23                     // we add the RollbarAppender so messages are sent to rollbar.
24                     // If it is not, we print a warning in the browser console.
25                     if (window.Rollbar) {
26                         Logger.addAppender(new RollbarAppender());
27                     } else {
28                         console.warn('Rollbar is not defined');  // eslint-disable-line no-console
29                     }
30                     // Configure logger to log only messages of level warning or above.
31                     Logger.setLevel(Logger.logLevel.warn);
32                 });
33             });
34         });
35     }
36 
37     if (environment.testing) {
38         aurelia.use.plugin('aurelia-testing');
39     }
40 
41     aurelia.start().then(() => aurelia.setRoot('app', document.body));
42 }

Loading Rollbar

Now we need to load the Rollbar script for Rollbar to actually work. Since in addition to logging messages with the Rollbar object, Rollbar can log uncaught exception, it is better to load it as early as possible (to detect errors when Aurelia is bootstraping for instance). To achieve this, I decided to put the configuration and quickstart script (from here) into a rollbar.js script located in the scripts folder. Then, since I use the CLI to build my project, I edited aurelia_project/aurelia.json to add "scripts/rollbar.js", in the prepend section of the vendor bundle. So the definition of the vendor bundle looks like:

 1 {
 2     "name": "vendor-bundle.js",
 3     "prepend": [
 4         "node_modules/bluebird/js/browser/bluebird.js",
 5         "scripts/polyfills.js",
 6         "scripts/rollbar.js",
 7         "node_modules/requirejs/require.js"
 8     ],
 9     "dependencies": [
10         "aurelia-binding",
11         "aurelia-bootstrapper",
12         "aurelia-dependency-injection",
13         "aurelia-event-aggregator",
14         "aurelia-framework",
15         "aurelia-history",
16         "aurelia-history-browser",
17         "aurelia-loader",
18         "aurelia-loader-default",
19         "aurelia-logging",
20         "aurelia-logging-console",
21         "aurelia-metadata",
22         "aurelia-pal",
23         "aurelia-pal-browser",
24         "aurelia-path",
25         "aurelia-polyfills",
26         "aurelia-route-recognizer",
27         "aurelia-router",
28         "aurelia-task-queue",
29         "aurelia-templating",
30         "aurelia-templating-binding",
31         "text",
32         {
33             "name": "aurelia-templating-resources",
34             "path": "../node_modules/aurelia-templating-resources/dist/amd",
35             "main": "aurelia-templating-resources"
36         },
37         {
38             "name": "aurelia-templating-router",
39             "path": "../node_modules/aurelia-templating-router/dist/amd",
40             "main": "aurelia-templating-router"
41         },
42         {
43             "name": "aurelia-testing",
44             "path": "../node_modules/aurelia-testing/dist/amd",
45             "main": "aurelia-testing",
46             "env": "dev"
47         }
48     ]
49 }

If you want to load Rollbar from another location, you can use the rollbarJsUrl property in the configuration object and give it the URL from which you want to load the script.

That's it. If you have any question or remark, please leave a comment.