Boomerang

Legacy Plugins For Single Page Apps

Why

Boomerang monitors Single Page App (SPA) navigations differently than how it monitors navigations on traditional websites.

On traditional websites, the browser completes a full navigation for every page. During this navigation, the browser requests the page’s HTML, JavaScript, CSS, etc., from the server, and builds the page from these components. Boomerang monitors this entire process.

On SPA websites, only the first page that the visitor loads is a full navigation. All subsequent navigations are handled by the SPA framework itself (i.e. AngularJS), where they dynamically pull in the content they need to render the new page. This is done without executing a full navigation from the browser’s point of view.

Boomerang was designed for traditional websites, where a full navigation occurs on each page load. During the navigation, Boomerang tracks the performance characteristics of the entire page load experience. However, for SPA websites, only the first page triggers a full navigation. Thus, without any additional help, Boomerang will not track any subsequent interactions on SPA websites.

To give visibility into SPA website navigations, there are several Boomerang plugins available for SPA frameworks, such as AngularJS, Ember.js and Backbone.js. When these plugins are enabled, Boomerang is able to track all of the SPA navigations beyond the first, initial navigation.

To do so, the Boomerang SPA plugins listen for several life cycle events from the framework, such as AngularJS’s $routeChangeStart. Once it gets notified of these events, the Boomerang SPA plugins start monitoring the page’s markup (DOM) for changes. If any of these changes trigger a download, such as a XHR, image, CSS, or JavaScript, then the Boomerang SPA plugins monitor those resources as well. Only once all of these new resources have been fetched do the Boomerang SPA plugins consider the SPA navigation complete.

For more information, see How mPulse XMLHttpRequest (XHR) and Single Page Application (SPA) Monitoring works.

If you are collecting ResourceTiming data in your SPA app, please also see how to manage the ResourceTiming buffer.

Terminology

  • Hard Navigation: the first navigation to the site, plus any of the work required to build the initial view. The Hard Navigation will track at least the length of onload‚Äč, but may also include the additional time required to load the framework (for example, Angular) and the first view. A SPA site will only have a SPA Hard Navigation, no “Page Load” beacons.
  • Soft Navigation: any navigation after the first Hard Navigation. A soft navigation is an “in-page” navigation where the view changes, but the browser does not actually fully navigate.

Metrics and Sessions

Page Groups, Custom Timers, Custom Metrics and Custom Dimensions are tracked on SPA websites the same way as on traditional websites (e.g. using XPath, Regex, JavaScript variables, cookies, CSS, etc).

Sessions are also tracked the same way as on traditional websites. The session length will increase every time the route (address bar) changes, and the session will be kept alive as long as user actions occur.

AngularJS

The mPulse Boomerang Angular plugin allows users to automatically monitor their Single Page App’s (SPA) navigations beyond the initial page load for AngularJS websites.

Implementing the AngularJS Plugin Code

In addition to the standard mPulse loader snippet, you will need to add an additional snippet within your AngularJS app, so Boomerang can hook into AngularJS events.

The following code snippet initializes the Boomerang AngularJS plugin and should be placed in your AngularJS’s app startup, by adding a .run() block with a $rootScope dependency.

/*
 * Installation:
 *
 * Somewhere in your AngularJS app or module startup, add a `.run()` block with
 * a `$rootScope` dependency.  In this run block, add the code below.
 *
 * For example:
 */
angular.module("app")
    .run(["$rootScope", function($rootScope) {
        var hadRouteChange = false;

        $rootScope.$on("$routeChangeStart", function() {
            hadRouteChange = true;
        });
        // The following listener is required if you're using ui-router
        $rootScope.$on("$stateChangeStart", function() {
            hadRouteChange = true;
        });

        function hookAngularBoomerang() {
            if (window.BOOMR && BOOMR.version) {
                if (BOOMR.plugins && BOOMR.plugins.Angular) {
                    BOOMR.plugins.Angular.hook($rootScope, hadRouteChange);
                }
                return true;
            }
        }

        if (!hookAngularBoomerang()) {
            if (document.addEventListener) {
                document.addEventListener("onBoomerangLoaded", hookAngularBoomerang);
            } else if (document.attachEvent) {
                document.attachEvent("onpropertychange", function(e) {
                    e = e || window.event;
                    if (e && e.propertyName === "onBoomerangLoaded") {
                        hookAngularBoomerang();
                    }
                });
            }
        }
    }]);

Page-Level Override for Partial AngularJS Apps

If your website includes both AngularJS and non-AngularJS pages, page-level overrides ensure the non-AngularJS pages still track performance data.

If all of your pages are using AngularJS:

  1. Select the Framework: AngularJS radio button in the mPulse Configure Web App dialog box.
  2. Insert the standard mPulse loader snippet.
  3. Implement the Boomerang AngularJS Plugin snippet.

If most of your pages are using AngularJS:

  1. Select the Framework: AngularJS radio button in the mPulse Configure Web App dialog box.
  2. Insert the standard mPulse loader snippet.
  3. Implement the Boomerang AngularJS Plugin snippet.
  4. Add the following snippet on page(s) that are not built with AngularJS:
window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.autorun = true;
BOOMR_config.Angular = {
    enabled: false
};

If only one or a few of your pages are using AngularJS:

  1. Select the Framework: None radio button in the mPulse Configure Web App dialog box.
  2. Insert the standard mPulse loader snippet.
  3. Implement the Boomerang AngularJS Plugin snippet.
  4. Add the following snippet on page(s) that are built with AngularJS:
window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.autorun = false;
BOOMR_config.Angular = {
    enabled: true
};

Ember.js

The mPulse Boomerang Ember plugin allows users to automatically monitor their Single Page App’s (SPA) navigations beyond the initial page load for Ember.js websites.

Currently, Ember-CLI is not supported.

Implementing the Ember.js Plugin Code

In addition to the standard mPulse loader snippet, you will need to add an additional snippet within your Ember.js app, so Boomerang can hook into Ember.js events.

The following code snippet initializes the plugin and should be placed after your EmberJS route definitions, replacing EMBER_APP_INSTANCE with the instance of your Ember app:

(function(){
    var app = EMBER_APP_INSTANCE; // EDIT THIS LINE

    var hadRouteChange = false;
    var hadRouteChangeToggle = function() {
        hadRouteChange = true;
    };

    if (app.ApplicationRoute) {
        app.ApplicationRoute.reopen({
            activate: hadRouteChangeToggle
        });
    } else {
        app.ApplicationRoute = Ember.Route.extend({
            activate: hadRouteChangeToggle
        });
    }

    function hook() {
        if (window.BOOMR && BOOMR.version) {
            if (BOOMR.plugins && BOOMR.plugins.Ember) {
                BOOMR.plugins.Ember.hook(app, hadRouteChange);
            }
            return true;
        }
    }

    if (!hook()) {
        if (document.addEventListener) {
            document.addEventListener("onBoomerangLoaded", hook);
        } else if (document.attachEvent) {
            document.attachEvent("onpropertychange", function(e) {
                e = e || window.event;
                if (e && e.propertyName === "onBoomerangLoaded") {
                    hook();
                }
            });
        }
    }
})();

Page-Level Override for Partial Ember.js Apps

If your website includes both Ember.js and non-Ember.js pages, page-level overrides ensure the non-Ember.js pages still track performance data.

If all of your pages are using Ember.js:

  1. Select the Framework: Ember.js radio button in the mPulse Configure Web App dialog box.
  2. Insert the standard mPulse loader snippet.
  3. Implement the Boomerang Ember.js Plugin snippet.

If most of your pages are using Ember.js:

  1. Select the Framework: Ember.js radio button in the mPulse Configure Web App dialog box.
  2. Insert the standard mPulse loader snippet.
  3. Implement the Boomerang Ember.js Plugin snippet.
  4. Add the following snippet on page(s) that are not built with Ember.js:
window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.autorun = true;
BOOMR_config.Ember = {
    enabled: false
};

If only one or a few of your pages are using Ember.js:

  1. Select the Framework: None radio button in the mPulse Configure Web App dialog box.
  2. Insert the standard mPulse loader snippet.
  3. Implement the Boomerang Ember.js Plugin snippet.
  4. Add the following snippet on page(s) that are built with Ember.js:
window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.autorun = false;
BOOMR_config.Ember = {
    enabled: true
};

Backbone.js

The mPulse Boomerang Backbone plugin allows users to automatically monitor their Single Page App’s (SPA) navigations beyond the initial page load for Backbone.js websites.

Implementing the Backbone.js Plugin Code

In addition to the standard mPulse loader snippet, you will need to add an additional snippet within your Backbone.js app, so Boomerang can hook into Backbone.js events.

The following code snippet initializes the plugin and should be placed before you call Backbone.history.start(), replacing BACKBONE_ROUTER_INSTANCE with the instance of your Backbone router:

(function(){
    var router = BACKBONE_ROUTER_INSTANCE; // EDIT THIS LINE
    var hadRouteChange = false;

    router.on("route", function() {
        hadRouteChange = true;
    });

    function hook() {
        if (window.BOOMR && BOOMR.version) {
            if (BOOMR.plugins && BOOMR.plugins.Backbone) {
                BOOMR.plugins.Backbone.hook(router, hadRouteChange);
            }
            return true;
        }
    }

    if (!hook()) {
        if (document.addEventListener) {
            document.addEventListener("onBoomerangLoaded", hook);
        } else if (document.attachEvent) {
            document.attachEvent("onpropertychange", function(e) {
                e = e || window.event;
                if (e && e.propertyName === "onBoomerangLoaded") {
                    hook();
                }
            });
        }
    }
})();

Page-Level Override for Partial Backbone.js Apps

If your website includes both Backbone.js and non-Backbone.js pages, page-level overrides ensure the non-Backbone.js pages still track performance data.

If all of your pages are using Backbone.js:

  1. Select the Framework: Backbone.js radio button in the mPulse Configure Web App dialog box.
  2. Insert the standard mPulse loader snippet.
  3. Implement the Boomerang Backbone.js Plugin snippet.

If most of your pages are using Backbone.js:

  1. Select the Framework: Backbone.js radio button in the mPulse Configure Web App dialog box.
  2. Insert the standard mPulse loader snippet.
  3. Implement the Boomerang Backbone.js Plugin snippet.
  4. Add the following snippet on page(s) that are not built with Backbone.js:
window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.autorun = true;
BOOMR_config.Backbone = {
    enabled: false
};

If only one or a few of your pages are using Backbone.js:

  1. Select the Framework: None radio button in the mPulse Configure Web App dialog box.
  2. Insert the standard mPulse loader snippet.
  3. Implement the Boomerang Backbone.js Plugin snippet.
  4. Add the following snippet on page(s) that are built with Backbone.js:
window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.autorun = false;
BOOMR_config.Backbone = {
    enabled: true
};

React

The mPulse Boomerang History plugin allows users to automatically monitor Single Page App’s (SPA) navigations beyond the initial page load for SPA frameworks that leverage the window.history object and the History API for keeping state and routing.

Implementing the History Plugin Code in React

In addition to the standard mPulse loader snippet, you will need to add an additional snippet within your SPA application code, so Boomerang can hook into the history object instance specific to your React-Router instance.

For React-Router where the history object is passed to the Router during instantiation, a route definition needs to be passed into BOOMR.plugins.History.hook() as the first parameter:

import { useBasename } from "history";
import createHashHistory from "history/lib/createHashHistory";
import createBrowserHistory from "history/lib/createBrowserHistory";

var history, hadRouteChange;

If the browser supports native HTML5 routing via history:

history = useBasename(createBrowserHistory)({
    basename: location.pathname
});

If the browser only supports Hash-based routing:

history = createHashHistory();

After the history variable has been setup, use this snippet, passing in the history object you just created to BOOMR.plugins.History.hook():

function hookHistoryBoomerang() {
    if (window.BOOMR && BOOMR.version) {
        if (BOOMR.plugins && BOOMR.plugins.History) {
            BOOMR.plugins.History.hook(history, hadRouteChange);
        }
        return true;
    }
}

if (!hookHistoryBoomerang()) {
    if (document.addEventListener) {
        document.addEventListener("onBoomerangLoaded", hookHistoryBoomerang);
    } else if (document.attachEvent) {
        document.attachEvent("onpropertychange", function(e) {
            e = e || window.event;
            if (e && e.propertyName === "onBoomerangLoaded") {
                hookHistoryBoomerang();
            }
        });
    }
}

The second parameter to BOOMR.plugins.History.hook() is the hadRouteChange variable, which tells the History plugin that a React route change had already occurred prior to when this instrumentation is starting.

If hadRouteChange is true, the History plugin will send performance information about that missed route change right away. If not, it waits for the first route-change.

To capture hadRouteChange in your React-Router application, use onEnter():

function onEnter() {
    hadRouteChange = true;
}

Once your history object is created, you can setup the Router and render it with ReactDOM:

import { render } from "react-dom";

render((
        <Router history={history} onEnter={onEnter}>
            <Route path="/" component={App}>
                <IndexRoute component={Home}/>
            </Route>
        </Router>
), document.body);

Page-Level Override for Partial React Apps

If your website includes both React and non-React pages, page-level overrides ensure the non-React pages still track performance data.

If all of your pages are using React:

  1. Select the Framework: React radio button in the mPulse Configure Web App dialog box.
  2. Insert the standard mPulse loader snippet.
  3. Implement the Boomerang React Plugin snippet.

If most of your pages are using React:

  1. Select the Framework: React radio button in the mPulse Configure Web App dialog box.
  2. Insert the standard mPulse loader snippet.
  3. Implement the Boomerang React Plugin snippet.
  4. Add the following snippet on page(s) that are not built with React:
window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.autorun = true;
BOOMR_config.History = {
    enabled: false
};

If only one or a few of your pages are using React:

  1. Select the Framework: None radio button in the mPulse Configure Web App dialog box.
  2. Insert the standard mPulse loader snippet.
  3. Implement the Boomerang React Plugin snippet.
  4. Add the following snippet on page(s) that are built with React:
window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.autorun = false;
BOOMR_config.History = {
    enabled: true,
    auto: false
};

Other SPAs

The mPulse Boomerang History plugin allows users to automatically monitor Single Page App’s (SPA) navigations beyond the initial page load for SPA frameworks that leverage the window.history object and the History API for keeping state and routing.

For SPA frameworks that use window.history, all you have to do is to add the standard mPulse loader snippet, and enable the History feature in the domain dialog under Single Page Application Framework.

If only a part of your site is using the SPA, you can use BOOMR_config to enable the History feature in your code:

window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.autorun = false;
BOOMR_config.History = {
    auto: true,
    enabled: true
};

When configured, it enables the History plugin in Boomerang, and tells it to automatically instrument the window.history object.

Excluding Certain Requests From Instrumentation

Whenever Boomerang intercepts an XMLHttpRequest, it will check if that request matches anything in the Boomerang XHR excludes list (BOOMR.xhr_excludes). If it does, Boomerang will not instrument, time, or beacon that request. If the excluded XHR happens during a SPA navigation, Boomerang will not track that XHR for purposes of extending the SPA navigation.

The exclude list is defined by creating an xhr_excludes object under the global window.BOOMR object, and adding in the URL parts that you would like to exclude from instrumentation. You can put any of the following in it:

  1. A full HREF
  2. A hostname
  3. A path

Example

BOOMR = window.BOOMR || {};

BOOMR.xhr_excludes = {
  "www.soasta.com":  true,
  "c.go-mpulse.net": true,
  "/api/v1/foobar":  true,
  "https://mpulse.soasta.com/dashboard/": true
};

In the above example, Boomerang will skip instrumentation for:

  • All URLs under www.soasta.com and c.go-mpulse.net
  • All URLs that exactly match the path /api/v1/foobar (regardless of the hostname or query string parameters)
  • The exact URL https://mpulse.soasta.com/dashboard/

Note that all of the above are exact matches (you cannot include wildcards).