Tracking Exceptions

Google Analytics Data Structure

To validate tracking of Exceptions, use the following Array in your Jasmine expectations:

['send', 'exception', { ─────────────────> 1&2. (Standard Google Analytics parameters)
   'exDescription': 'DatabaseError', ────> 3. Exception Description
   'exFatal': false, ────────────────────> 4. Indicates whether the exception was fatal
   'appName', 'Demo_Application', ───────> 5. Application Name
   'appVersion', '1.0' ──────────────────> 6. Application Version Number
}]

Sample Jasmine Spec File

Using direct ga() calls, checking proper tracking of Exceptions could be done in the following manner:

describe('The Google Analytics "Exception" tracking', function () {
   beforeEach(function () {
      // Load the page to test:
      browser.get('index.html');

      // Register the Google Analytics Event Data Interceptor:
      browser.driver.registerGoogleAnalyticsEventDataInterceptor();
   });
 
   it('should register an Exception when causing an application crash', function (done) {
      // Get the "LastEvent" object back from the browser:
      browser.driver.executeScript(function () {
         return window.GAWebTester.getLastEvent();
      })
      .then(
         // Validate the content of the "LastData" object:
         function successCallback (LastData) {
            expect( LastData ).toEqual( ['send', 'exception', {
               'exDescription': 'DatabaseError',
               'exFatal': false,
               'appName': 'Demo_Application',
               'appVersion': '1.0'
            }] );
         },
         // If there was an error getting back the "LastData" object from the browser, fail the test:
         function errorCallback (error) {
            fail('Should not have received Error: ' + JSON.stringify(error));
         }
      )
      .then(done);
   });
});

To manually simulate an Error in the browser, the following code will send a DatabaseError Exception:

/**
 * Trigger an Exception in the Browser by throwing an Error then catching
 * it with a "ga()" call.
 *
 * @return {void}
 */
function triggerGoogleAnalyticsException () {
   try {
      throw new Error('DatabaseError.');
   } catch (err) {
      // Send the exception to Google Analytics:
      ga('send', 'exception', {
         'exDescription': err.message,
         'exFatal': false,
         'appName': 'Demo_Application',
         'appVersion': '1.0'
      });
   }
}

// Trigger a "crash" in the browser:
triggerGoogleAnalyticsException();

Logging JavaScript Errors

Few people know about this, but the free version of Google Analytics supports exception logging. It might be caused by the fact that Google Analytics does not provide any built-in Report surfacing that information. Fortunately, that does not prevent you from making your own.

First, you will need to either:

  • Include the following JavaScript code snippet into your application.
  • Use Google Tag Manager to inject it into every single page of your site.

You can customize a few things:

  • appName (string): Name of the application you are tracking
  • appVersion (string): Version number and/or deployment date of the application
  • exFatal (boolean): “true” or “false”, depending on the Type of Error received

You can test that the everything is properly set up by opening your browser’s developer console and typing throw new Error('Crash!'), then checking your Custom Report a few minutes later to make sure your Error is logged.

For the complete walkthrough of how to set it up, and a free Error Reporting Dashboard, see the full post on philippesawicki.com.

/**!
 * Send JavaScript error information to Google Analytics.
 * 
 * @param  {Window}           window  A reference to the "window".
 * @param  {Object|undefined} options An object containing optional "applicationName" and
 *                                    "applicationVersion" strings.
 * @return {void}
 * @author Philippe Sawicki (https://github.com/philsawicki)
 * @copyright Copyright 2015 Philippe Sawicki (http://philippesawicki.com)
 */
(function (window, options) {
   // Retain a reference to the previous global error handler, in case it has been set:
   var originalWindowErrorCallback = window.onerror;
       
   /**
    * Log any script error to Google Analytics.
    *
    * Third-party scripts without CORS will only provide "Script Error." as an error message.
    * 
    * @param  {String}           errorMessage Error message.
    * @param  {String}           url          URL where error was raised.
    * @param  {Number}           lineNumber   Line number where error was raised.
    * @param  {Number|undefined} columnNumber Column number for the line where the error occurred.
    * @param  {Object|undefined} errorObject  Error Object.
    * @return {Boolean}                       When the function returns true, this prevents the 
    *                                         firing of the default event handler.
    */
   window.onerror = function customErrorHandler (errorMessage, url, lineNumber, columnNumber, errorObject) {
       // Send error details to Google Analytics, if the library is already available:
       if (typeof ga === 'function') {
           // In case the "errorObject" is available, use its data, else fallback 
           // on the default "errorMessage" provided:
           var exceptionDescription = errorMessage;
           if (typeof errorObject !== 'undefined' && typeof errorObject.message !== 'undefined') {
               exceptionDescription = errorObject.message;
           }
 
           // Format the message to log to Analytics (might also use "errorObject.stack" if defined):
           exceptionDescription += ' @ ' + url + ':' + lineNumber + ':' + columnNumber;
 
           // Data Object to send to Google Analytics:
           var exOptions = {
               exDescription: exceptionDescription,
               exFatal: false // Some Error types might be considered as fatal.
           };
 
           // Format additional Data Object Properties, if any option given:
           if (typeof options !== 'undefined') {
               if (typeof options.applicationName !== 'undefined') {
                   exOptions.appName = options.applicationName;
               }
               if (typeof options.applicationVersion !== 'undefined') {
                   exOptions.appVersion = options.applicationVersion;
               }
           }
 
           // Send Data Object to Google Analytics:
           ga('send', 'exception', exOptions);
       }
 
       // If the previous "window.onerror" callback can be called, pass it the data:
       if (typeof originalWindowErrorCallback === 'function') {
           return originalWindowErrorCallback(errorMessage, url, lineNumber, columnNumber, errorObject);
       }
 
       // Otherwise, let the default handler run:
       return false;
   };
})(window, {
    applicationName: 'Application_Name', // Optional "Application Name" parameter (set your own).
    applicationVersion: '1.0'            // Optional "Application Version" parameter (set your own).
});
 
// Generate an error, for demonstration purposes:
//throw new Error('Crash!');