Review: Track:js Eases Front-end Error Detection

Marcello La Rocca, Data Visualization Engineer, SwiftIQ
Jul. 08 2014, 11:10AM EDT

Track:js is an intuitive tool that can be used to track client-side JavaScript errors in production, as well as during development. While there are other products that provide functionality along the same lines, the Track:js tool stands apart in many (good) ways.

Created by Todd Gardner, Eric Brandes and Nick Pelton, Track:js made its official debut in April. The basic version is free, although it has some limitations: 10,000 hits (that is, page loads, not errors) per month and a history limited to 24 hours. The Startup plan costs $30 per month and allows 500,000 hits per month and eight days of history; the Business plan costs $100 per month and allows 10 million hits per month and 15 days of history. An unlimited Enterprise version is also available, with pricing negotiable on a case-by-case basis.

Developers can test out the Business version of Track:js for free for 30 days. To put the tool’s features and its API through its paces, we performed hands-on tests on a midsize application, http://thound.org.

Setting our ‘sites’ on Track:js
Any Javascript developer has his or her eyes open to news about new JS libraries and tools, but when we first spotted a tweet about the new tool we were intrigued.

As JS developers, tracking errors is kind of what we do. Well, OK, that’s over-simplistic: Today, we have many good tools at our disposal to help us catch errors as early as possible, starting with test-driven development (JasmineSeleniumQUnitsZombie--likely any JS developer’s best friends) and including development tools such as Chrome Dev Tools and Mozilla FireBug.

But what about front-end error tracking after development? Some Web applications have bug reporting tools and bug dictionaries, while others rely on direct feeds from the users. But all of them require that users reconstruct (or try to reconstruct) the sequence of actions and the specific conditions (in a single word, the context) that led to or preceded an error.

Reproducing errors can be very complicated. Users sometimes report far too many or too few details. Or sometimes they omit key elements, such as which browser or browser version they were using when an error occurred. Further, few users are capable of reporting the stack trace at the moment of the failure.

This is where Track:js kicks in. According to the Track:js product Website, the “TrackJS analytics platform captures millions of errors from around the world and aggregates them into meaningful dashboards and reports to help you understand how your app is changing over time. We separate the signal from the noise so you don’t have to.”

Indeed, based on our tests, most applications will benefit from the use of Track:js. Even the free version will likely help organizations to find hidden bugs during beta testing. However, you’ll likely find that you reach 10,000 page loads in a heartbeat, and once your hit-to-error rate drops, this means you won’t be able to catch a lot of bugs. Whether you decide to subscribe to a payment plan is a business decision that goes beyond the boundaries of this review. But, we can tell you to consider the cost per bug for your business, the cost for solving each bug, and how this would be influenced by a tracking system. In practice, the bigger your application is and the more complicated your code is, the more a tool like Track:js makes sense.

Getting started
To install Track:js, you add track.js script to your Web page. You can either download the script from the online repository or host it locally. (In the latter case, you need to add a short extra configuration script before loading it.)

Either way, it is crucial that you load this script before any other script in your page; otherwise, you won’t be able to perform tracking on your application. Once the script has been added, Track:js starts tracking your application, automatically logging every error to your dashboard.

However, if you stop there you will be missing the best features of this product—most notably, the information logged would be missing the stack trace.

Let’s say data will be recorded for console, network activity and visitor events, but only when an error occurs will data relative to the error context be transmitted to Track:js servers.

There are five conditions that trigger the logging process :

Errors, by default, bubble up to the global object, so the stack trace will be lost. However, Track:js provides several other ways to track errors:

  1. Using try/catch around specific sections of the code, and,  when errors happen, notifying your dashboard using window.trackJs.track(err)
    For instance: try {
    // do something that could break!
    } catch (e) {
    trackJs.track(e); //can either pass an actual Error
    ​ //object or a string

    }

This is convenient only if you have to repeat it in your code just a few times. Obviously, you can’t wrap your whole application in a try catch, because the error, once again, would bubble up from the function where the error happened until it hits the closest try. The stack trace in between would still be lost.

  1. window.trackJs.attempt(function, [context], [arguments])
    /**
      * Invokes the provided function within a try/catch wrapper
      * that forwards the error to TrackJS.
      *
      * @method attempt
      * @param {Function} func The function to be invoked.
      * @param {Object} context The context to invoke the function with.
      * @param {...} Additional arguments passed to the function.
      * @returns Output of the function.
      */
    trackJs.attempt(function (message) {
      throw new Error(message);
    }, this, "message");
    // Error is tracked and rethrown

This method allows developers to run any function inside an artificial, transparent context, catching and tracking errors that might happen. Basically, it’s a shortcut for the try/catch above.

  1. window.trackJs.watch(function, [context])
    /**
      * Returns a wrapped and watched version of the function to automatically
      * catch any errors that may arise.
      *
      * @method watch
      * @param {Function} func The function to be watched.
      * @param {Object} context The context to invoke the function with.
      * @returns Output of the function.
      */
    var wrappedFn = trackJs.watch(function (a, b) {
      throw new Error(a + b);
    }, this);
    
    wrappedFn("5", "50");
    // Error is tracked and rethrown

If you need to call the same function several times, this is your best shot: During our tests, this method enabled us to build a wrapper around our function, save a reference to the wrapper and replace every call to the former function with a call to its watcher.
 
If we needed to add tracking to an existing application, the modification was very quick: We just named the var containing the reference to the wrapper with the same name the original function had. As mentioned earlier, tracking is completely transparent and will never block your code. (It is worth mentioning that Track:js will disable itself on browsers where it can’t run appropriately.)

  1. window.trackJs.watchAll(object, [string...])
    /**
      * Wrap and watch all of the functions on an object that will
      * automatically catch any errors that may arise.
      *
      * @method watch
      * @param {Object} obj The object to inspect for functions.
      */
    trackJs.watchAll(myModel);

As a further shortcut, it is possible to track with a single instruction all the methods in an object/module. This is actually very convenient because you can even exclude specific methods that won’t be tracked by passing their names as parameters.

It is good to know that by including Track:js in your page, you’ll have a good level of certainty that the console object will work properly, regardless of the browser, since it will be replaced by Track:js’s wrapper by default.

It is also possible to configure Track:js so that the original console is left untouched. In that case, window.trackJs.log , window.trackJs.debug, and so on will be available to explicitly log messages to your dashboard.

The last 10 console messages (either implicitly or explicitly logged) will be stored and sent to Track:js servers only when an error occurs. This gives developers great control, basically allowing them to create custom events--sort of “watch points”--where you can record information about key variables or notes about your workflow.

Drilling down into Track.js’s features
Track:js offers a number of features that will prove useful to developers. These include, but are not limited to:

Dashboard panel

The Dashboard panel is key to the Track.js tool. It provides users with a nice, overall view of activity across the entire website, while at the same time enabling developers to examine specific actions in more detail. The bird’s-eye view, however, is very useful and will help developers get a grasp on how a whole project is working.

In the screen shown above, the overall ratio of error per page load (since the project started being monitored) was 12%. However, at the time of testing, there hadn’t been any errors in 24 hours--with a reduction rate of 100% in comparison with the day before.

In the second row, the total number of page loads and errors is shown, along with a tracking timeline. The timeline’s value increases over, you guessed it, time, as errors caught on different days pile up and aggregate.

The three boxes at the bottom of the page provide error information grouped by browser, by URL and by messages. The way this information is organized is very useful because it highlights hidden patterns and allows developers to work on the most commonly occurring bugs first.

Recent Errors Panel

The “Recent” panel provides a chronological view of errors tracked. Using the search bar in the top-right corner, we could filter errors by keyword, domain and time range. As mentioned earlier, the number of days errors are kept in the database depends on the subscription purchased.

By clicking on any error, the application shows the “error panel,” where the specific details of an error are shown:

In the screen above, at right, there are two panels: The top panel tells when an error happened and provides details about the request and environment in which the error happened. The bottom panel provides a list of libraries loaded by the page that triggered the error.

The bigger panel at left shows the timeline for the error, where time “0” coincides, of course, with the moment the page is loaded. At its very top, you can see a summary visualizing the number of user interactions (green), console messages (orange) and http calls (blue). Details about these three categories, and errors, are shown according to their chronological order in the timeline below.

Clicking on the Stack Trace tab, instead, displays the stack trace as captured by the monitoring instructions added to your code.

We ran into one small issue at the time we conducted the review: The stack trace couldn’t take advantage of source maps, which will make it a little harder to spot bugs in production code if you minified it (as you should). The Track:js team has confirmed that it is working on source maps support, which will be added in future updates.

As mentioned above, the closer the monitoring code (try/catch, trackJs.attempt or trackJs.watch) is to the point where the error was generated, the more detailed the captured stack trace will be.

To further understand this principle, consider the following example:
 

function h () {
     throw new TypeError();
}
function f () {
    ...
    h();
    ...
}
var g = trackJs.watch(f);
g();
 

When h throws an exception, it bubbles up to and then to g, that will catch and track the error, but the stack trace inside (in this case the call to h, but in general any stack of untracked calls started inside f), will be lost.

Another thing you should be careful about, especially if you hadn’t designed your application knowing from the beginning you’d use Track:js, is that existing try/catch blocks could prevent errors from being tracked: As soon as an exception is caught, in fact, it will stop bubbling up, and if it doesn’t reach a tracking instruction, it won’t be recorded. A slight modification to previous example helps clarifying the concept:
 

function h () {
     throw new TypeError();
}
function f () {
    ...
    try {
        h();
    } catch (err) {
        console.log(err);
        return;
    }
    ...
}
var g = trackJs.watch(f);
g();

If a rudimentary logging had been used, and the chosen behavior in case of exceptions was simply stopping the execution of your method, the exception would not bubble up any further and would basically go unnoticed. This is the worst-case scenario, and the situation that requires more care and maintenance from developers. Nonetheless, the solution is simple:

function f () {
    ...
    try {
        h();
    } catch (err) {
        trackJs.track(err);
        return;
    }
    ...
}

The downside is just that, for legacy code, you’ll have to take care of it for every error handling instruction present in the original code.

Finally, on the left side of the Timeline/Stack trace tabs, you’ll see a handy menu that lets users add a particular error to the starred list and easily find it later, delete it or delete all similar messages, according to specific criteria.

  • Update: version 2.0.0 of Track.js introduced a major upgrade of the JavaScript tracker, that is now a lot more powerful and flexible. Details on these new features are explained in this post on Track.js' blog.

Other panels
Other panels in the tool show various groupings of errors in the database, following all the criteria we saw in the main dashboard (the difference being that in the dashboard only a handful of items is shown for each criterion), plus a few more, like grouping by visitor or by date (which is, however, also shown in the “Errors and Hits Timeline” widget in the main dashboard).

Track.js in context
Track:js is a clean and easy-to-use instrument that promises to make life easier for front-end developers. There are other solutions available, but most are more focused on tracking performance than errors (like Google Analytics) or are not as thorough as Track,js (like New Relic’s Qbaka plugin, which is tailored for error handling). With Google Analytics, it is possible to define custom events and use them to track errors (see here), but the result is not, by any means, comparable to the cleanness and usability of Track:js. For example, Google Analytics does not offer grouping, stack traces or user actions.

Other options require much more expensive subscriptions, without free options (Raygun, for example, offers advanced features like GitHub integration).

We have also been impressed by the effort the {Track:js} team is putting into improving its product-- in particular, the features that help customers understand which errors they should get priority and the improvements they are making to the information gathered about stack trace and context for every error.

Marcello La Rocca As a developer I'm focusing on JavaScript, Python and Java (Android), but I have a weakness for algorithms. Lately, apparently I also became a tech blogger! My personal blog: mlarocca.github.io - Follow me on Google+

Comments