NAV

Introduction

Boomerang is a JavaScript library for Real User Monitoring (commonly called RUM).

Boomerang measures the performance characteristics of real-world page loads and interactions.

The documentation on this page is for mPulse’s Boomerang.

General API documentation for Boomerang can be found at docs.soasta.com/boomerang-api/.

Implementation

Once you’ve signed up for a mPulse account, you will be given an API key.

To integrate mPulse into your site, you will need to include the mPulse Loader Snippet, which loads Boomerang in a performant way.

mPulse Loader Snippet

Paste the following code snippet into every page of your site at the top of the HEAD, but after sensitive META tags.

Replace [API KEY GOES HERE] with your mPulse API key.

<script>
(function(){
    // Boomerang Loader Snippet version 10
    if (window.BOOMR && (window.BOOMR.version || window.BOOMR.snippetExecuted)) {
        return;
    }

    window.BOOMR = window.BOOMR || {};
    window.BOOMR.snippetExecuted = true;

    var dom, doc, where, iframe = document.createElement("iframe"), win = window;

    function boomerangSaveLoadTime(e) {
        win.BOOMR_onload = (e && e.timeStamp) || new Date().getTime();
    }

    if (win.addEventListener) {
        win.addEventListener("load", boomerangSaveLoadTime, false);
    } else if (win.attachEvent) {
        win.attachEvent("onload", boomerangSaveLoadTime);
    }

    iframe.src = "javascript:void(0)";
    iframe.title = "";
    iframe.role = "presentation";
    (iframe.frameElement || iframe).style.cssText = "width:0;height:0;border:0;display:none;";
    where = document.getElementsByTagName("script")[0];
    where.parentNode.insertBefore(iframe, where);

    try {
        doc = iframe.contentWindow.document;
    } catch (e) {
        dom = document.domain;
        iframe.src = "javascript:var d=document.open();d.domain='" + dom + "';void(0);";
        doc = iframe.contentWindow.document;
    }

    doc.open()._l = function() {
        var js = this.createElement("script");
        if (dom) {
            this.domain = dom;
        }
        js.id = "boomr-if-as";
        js.src = "//c.go-mpulse.net/boomerang/" +
        "[API KEY GOES HERE]";
        BOOMR_lstart = new Date().getTime();
        this.body.appendChild(js);
    };
    doc.write('<bo' + 'dy onload="document._l();">');
    doc.close();
})();
</script>

Configuration Overrides

When a configuration override is required, the window.BOOMR_config object should be defined above the mPulse loader snippet:

<script>
window.BOOMR_config = window.BOOMR_config || {};

// set global options
window.BOOMR_config.autorun = false;

// set a plugin's options
window.BOOMR_config.History = {
    enabled: true,
    auto: true
};

(function(){
    // ...
    // loader snippet above
    // ...
})();
</script>

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.

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

Terminology

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 AngularJS 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.js 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.js 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 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 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:

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

Errors

The Boomerang Errors plugin automatically captures JavaScript and other errors from your web application.

Sources of Errors

When the Errors plugin is enabled, the following sources of errors are captured:

These are all enabled by default, and can be manually turned off.

Supported Browsers

The Errors plugin can be enabled for all browsers, though some older browsers may not be able to capture the full breadth of sources of errors.

Notable browsers:

Manually Sending Errors

Besides automatically capturing errors from onerror, XMLHttpRequest, console.error or event handlers such as setTimeout, you can also manually send errors.

There are three ways of doing this as follows:

Dealing with Script Error

When looking at JavaScript errors, you will likely come across the generic error message: Script error.

Script Error. is the message that browsers send to the window.onerror global exception handler when the error was triggered by a script loaded from a different (cross) origin. window.onerror is used by Boomerang so that it gets notified of all unhandled exceptions.

The Script Error. string is given instead of the real error message and does not contain any useful information about what caused the error. In addition, there is no stack associated with the message, so it’s impossible to know where or why the error occurred.

Browsers mask the real error message for cross-origin scripts due to security and privacy concerns - they don’t want to leak sensitive information in the message or stack. The only thing that window.onerror knows for cross-origin scripts is that an error occurred, not where or why.

Example

For an example of where you’d see Script Error., consider the following code that lives on website.com:

<html>
    <head>
        <title>website.com</title>
    </head>
    <body>
        <script>
            window.onerror = function(message, url, line, column, error) {
                console.log("window.onerror: " + message);
                console.log((error && error.stack) ? error.stack : "(no stack)");
            };
        </script>
        <script src="my-script.js"></script>
        <script src="https://anothersite.com/my-script.js"></script>
    </body>
</html>

Assume my-script.js is the same file being served from both website.com and anothersite.com:

function runCode() {
    a = b + 1;
}

runCode();

When my-script.js is loaded from website.com, it will be executed twice:

  1. First on the same-origin, where we’ll see the full error message followed by the stack:

    window.onerror: Uncaught ReferenceError: b is not defined
    
    ReferenceError: b is not defined
        at runCode (my-script.js:2)
        at my-script.js:5
    
  2. Then, it will be loaded from https://anothersite.com/my-script.js, which will be considered cross-origin and only Script Error. will be logged:

    window.onerror: Script error.
    
    (no stack)
    

As you can see, browser shares the full details of the exception when it’s served from the same origin as the website, but if it’s served from any other origin, it will be considered cross-origin and no details will be shared.

Note that while the browser only shares Script Error. to window.onerror for cross-origin scripts, if you have browser developer tools open, the browser will show you the full error message in the Console. This is because there aren’t any security or privacy concerns for a developer looking at their own machine’s information.

The screenshot below shows what the Chrome Developer tools look like for the above code:

  1. The first (same-origin) error message is written to the console.log() (in black) followed by the browser developer tools logging the same error (in red).

  2. The second (cross-origin) error only shows Script error. in the console.log() (in black) with no stack, but the browser developer tools show the full message and stack (in red):

When You’ll See Script Error

Unfortunately Script Error. will be shown in many legitimate use-cases, such as:

  1. When serving your website’s JavaScript from a CDN (since it will be coming from a different origin)

  2. When loading a library such as jQuery or Angular from their CDN, i.e. Google’s Hosted Libraries or cdnjs

  3. When a third-party script loads from another domain

The good news is that in many of these cases, there are changes you can make to ensure the full error message and stack are shared with window.onerror.

Fixing Script Error

To ensure a cross-origin script shares full error details with window.onerror, you’ll need to do two things:

  1. Add crossorigin="anonymous" to the <script> tag

    The crossorigin="anonymous" attribute tells the browser that the script should be fetched without sending any cookies or HTTP authentication

  2. Add the Access-Control-Allow-Origin (ACAO) header to the JavaScript file’s response.

    The Access-Control-Allow-Origin header is part of the Cross Origin Resource Sharing (CORS) standard.

    The ACAO header must be set in the JavaScript’s HTTP response headers.

    An example header that sets ACAO for all calling origins would be:

    Access-Control-Allow-Origin: *

If both conditions are true, cross-origin JavaScript files will report errors to window.onerror with the correct error message and full stack.

The biggest challenge to getting this working is that (1) is within the site’s control while (2) can only be configured by the owner of the JavaScript. If you’re loading JavaScript from a third-party, you will need to encourage them to add the ACAO header if it’s not already set. The good news is that many CDNs and third-parties set the ACAO header already.

Workarounds for Third Parties that aren’t sending ACAO

One way to help monitor for errors coming from third-party scripts that aren’t setting ACAO (and aren’t within your control) is by manually wrapping calls to any of the third-party script’s functions in a try {} catch {}.

try {
    // calls a cross-origin script that doesn't have ACAO
    runThirdPartyCode();
} catch (e) {
    // report on error with e.message and e.stack
}

If runThirdPartyCode() causes any errors, the catch {} handler will get the full details of the exception.

Unfortunately this won’t work for functions that are executed in the third-party script as a result of browser events or callbacks (since you’re not wrapping them).

When using Boomerang to monitor JavaScript errors, Boomerang automatically wraps some of the built-in browser APIs such as setTimeout, setInterval and addEventListener with a minimal-overhead wrapper. It does this to help ensure as many cross-origin exceptions as possible have full stack details. You may also do this manually via BOOMR.plugin.Errors.wrap(function).

Why is Boomerang in my Error Stack?

When looking at error reports, you may find errors that have a function in boomerang.js (or /boomerang/) on the stack. Why is that? Is Boomerang causing errors on your site?

One of the ways that Boomerang is able to monitor and measure your site’s performance is by wrapping itself around some of the core browser APIs. Boomerang only does this in a few places, if absolutely necessary – namely, when the browser doesn’t provide a native “monitoring” interface for something that needs to be tracked.

One example is for XMLHttpRequests, as there are no browser APIs to monitor when XHRs load. To monitor XHRs, Boomerang swaps in its own window.XMLHttpRequest object, wrapping around the native methods. When an XHR is created (via .open()), the lightweight Boomerang wrapper is executed first so it can log a start timestamp. When the XHR finishes (via a readyState change), Boomerang can log the end timestamp and report on the XHR’s performance.

Examples of Boomerang wrapping native methods include:

All of these wrapped functions come into play when you see an error stack with a boomerang.js function in it.

Often, the boomerang.js function will be at the bottom of the stack (the first function called). This does not mean Boomerang caused the error, merely that the monitoring code was running before the error occurred. The actual error happens towards the top of the stack – the function that ran and threw the exception.

Let’s look at some examples:

Cannot read property 'foo' of undefined at thirdPartyTwo (https://thirdparty.com/core.js:1:100)
at thirdPartyOne (https://thirdparty.com/core.js:1:101)
at runThirdParty (https://thirdparty.com/core.js:1:102)
at xhrCallback (http://website.com/site.js:2:200)
at XMLHttpRequest.send (https://c.go-mpulse.net/boomerang/XXXXX-XXXXX-XXXXX-XXXXX-XXXXX:3:300)

In the above example, Boomerang is monitoring XMLHttpRequests. An XHR was loaded on the site, and during the XHR callback, an exception was thrown. Even though /boomerang/ is listed here, the error was caused by code in the XHR callback (xhrCallback eventually calling thirdPartyTwo).

Here’s a second example:

Reference error: a is not defined at setTimeout (http://website.com/site.js:1:200)
at BOOMR_plugins_errors_wrap (http://c.go-mpulse.net/boomerang/XXXXX-XXXXX-XXXXX-XXXXX-XXXXX:3:300)
at onclick (http://website.com/site.js:1:100)

In the above example, JavaScript Error Reporting is enabled and an exception was thrown in a setTimeout() on the website. You can see the BOOMR_plugins_errors_wrap function is near the top of the stack, but this is merely the error tracking code. All it did was wrap setTimeout to help ensure that cross-origin exceptions are caught. It was not the actual cause of the site’s error.

Here’s a third example:

Error: missing argument 1 at BOOMR.window.console.error (https://c.go-mpulse.net/boomerang/XXXXX-XXXXX-XXXXX-XXXXX-XXXXX:3:300)
at u/< (https://website.com/site.js:1:100)
at tp/this.$get</< (https://website.com/site.js:1:200)
at $digest (https://website.com/site.js:1:300)
at $apply (https://website.com/site.js:1:400)
at ut (https://website.com/site.js:1:500)
at it (https://website.com/site.js:1:600)
at vp/</k.onload (https://website.com/site.js:1:700)

In the above example, JavaScript Error Reporting is enabled and has wrapped console.error. The minified function u/< must be logging a console.error, which executes the Boomerang wrapper code, reporting the error.

In summary, if you see Boomerang functions in error stacks similar to any of the ones listed below, it’s probable that you’re just seeing a side-effect of the monitoring code:

Configuration Options

Enabling

The Errors plugin is disabled by default, but can be enabled via BOOMR_config:

window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.Errors = {
   enabled: true,
   monitorTimeout: false,
   monitorEvents: false
};

Sources

Each of the sources of errors can be turned off manually via the following monitor* options:

For example:

window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.Errors = {
    monitorGlobal: true,  // onerror
    monitorNetwork: true, // XHRs
    monitorConsole: true, // window.console.error
    monitorEvents: true,  // addEventListener
    monitorTimeout: true, // setTimeout, setInterval
};

Error callback

You can specify an onError function that the Errors plugin will call any time an error is captured on the page.

If your onError function returns true, the error will be captured. If your onError function does not return true, the error will be ignored.

For example:

window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.Errors = {
    onError: function(err) {
        if (err.message && err.message.indexOf("internally handled")) {
            return false;
        }
        return true;
    }
};

When to Send Errors

By default, errors captured during the page load will be sent along with the page load beacon. Errors that happen after the page load will not be captured or sent.

To enable capturing of errors after page load, you need to set sendAfterOnload to true. If set, errors that happen after the page load will be sent at most once every sendInterval (which defaults to 1 second) on a new beacon.

For example:

window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.Errors = {
    sendAfterOnload: true,
    sendInterval: 5000
};

How Many Errors to Capture

The Errors plugin will only capture up to maxErrors (defaults to 10) distinct errors on the page.

Please note that duplicate errors (those with the same function name, stack, and so on) are tracked as single distinct error, with a count of how many times it was seen.

You can increase (or decrease) maxErrors. For example:

window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.Errors = {
    maxErrors: 2
};

Third Party Analytics (Big Beacon)

The Boomerang TPAnalytics plugin automatically captures third party analytics Session IDs and campaign information from your web application.

Third party analytics vendors currently supported:

Configuration Options

Enabling

The TPAnalytics plugin is disabled by default, but can be enabled via BOOMR_config:

window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.TPAnalytics = {
     enabled: true
};

How-Tos

Managing the ResourceTiming Buffer

ResourceTiming is a browser performance API that gathers accurate performance metrics about all of the resources fetched during the page load, such as images, CSS and JavaScript. Boomerang can capture this data automatically. The resources can then be visualized in the mPulse Waterfall dashboards.

By default, the Resource Timing API only tracks the first 150 resources on a page. While this limit can be manipulated by the developer in order to track more resources via window.performance.setResourceTimingBufferSize(), there are performance trade-offs (additional memory consumption) when doing this, so Boomerang doesn’t make these changes automatically.

If you are using one of the Boomerang SPA plugins, the browser might hit the 150 limit quickly, as the browser will not clear the resources for SPA navigations. Therefore, you may want to increase the buffer size or clear the resources every time a beacon is sent.

The following code examples show how you can increase the limit, or clear the resources after each Boomerang beacon.

Set the Resource Timings Buffer

To increase the ResourceTiming buffer size above the default of 150, you can use window.performance.setResourceTimingBufferSize(n):

(function(w){
    if (!w ||
        !("performance" in w) ||
        !w.performance ||
        !w.performance.setResourceTimingBufferSize) {
        return;
    }

    w.performance.setResourceTimingBufferSize(<size>);
})(window);

Clear the Resource Timings Buffer

To clear the ResourceTimings buffer on each beacon, you can use window.performance.clearResourceTimings():

(function(w){
    if (!w ||
        !("performance" in w) ||
        !w.performance ||
        !w.performance.clearResourceTimings) {
        return;
    }

    document.addEventListener(
        "onBoomerangBeacon",
        w.performance.clearResourceTimings.bind(w.performance));
})(window);

Cookies

mPulse Session information

When an end-user browses to a site that’s instrumented with mPulse, Boomerang generates an RT cookie. It contains various pieces of information about the user’s session, as viewed by Boomerang. Here is an example:

nu=https%3A%2F%2Fwww.website.com%2Fus%2Fen%2Fweb-and-mobile-performance.jsp&dm=website.com&si=fe68c9ef-c89b-4918-b18d-b7faebd22524&ss=1500590776086&sl=1&tt=1353&obo=0&sh=1500590778761%3D1%3A0%3A1353&cl=1500590808877&bcn=%2F%2F36f10833.akstat.io%2F&r=https%3A%2F%2Fwww.website.com%2F&ul=1500590809249&hd=1500590809568

The individual parameters are used as follows:

Field Description
bcn The URL that beacons will be sent to.
cl The timestamp of the most recent click event (epoch millis).
dm The mPulse-instrumented domain.
hd The timestamp of the previous page’s pagehide event (epoch millis).
nu The URL of a link on the current page that the user clicked on/submitted a form to in order to leave this page.
obo The number of pages visited in this session where Boomerang could not measure the page load time.
r The URL of the previous page visited in this session.
rl Rate limited flag. 1 if rate limited.
se Session expiry length (in seconds).
sh Session debugging information for the last 5 viewed pages [no longer used].
si The unique ID for this session.
sl The number of pages visited in the session (prior to the current page).
srst Details of the session prior to it being reset for expiry.
ss The session start time (epoch millis).
tt The sum of all page load times within this session.
ul The timestamp of the previous page’s unload event (epoch millis).

The RT cookie is never sent to mPulse servers, but is instead assigned to the domain of the site being instrumented (e.g. website.com). It’s used exclusively on the client side, by Boomerang.

The cookie is persistent, not a session cookie, and expires after 7 days of inactivity. This is because we want sessions to continue even if the end-user closes their browser and then returns within 30 minutes.

If the mPulse customer disables session tracking for their site, then Boomerang will not generate the RT cookie.

mPulse bandwidth testing information

When an end-user browses to a site that’s instrumented with mPulse and has bandwidth testing enabled, Boomerang generates a BA cookie.

Troubleshooting

The following guides help you to troubleshoot Boomerang issues.

Browser Developer Tools

Many Boomerang issues will need to be debugged using Browser Developer Tools.

DevToolsSecrets.com has guides for how to use all of the common developer tools.

Generally, they can be opened via F12 or Ctrl-Shift-I in Windows, or Cmd-Option-I on the Mac.

Running Boomerang in a Development Environment

By default, mPulse will disable itself it is run on any of the following host names:

mPulse disables itself so development environments (and saved pages) don’t mix in to production performance data.

If you are using a local development environment and would still like to send beacons, you have two options:

  1. Add window.BOOMR_LOGN_always = true to the page
  2. Run on an aliased hostname

Using BOOMR_LOGN_always

If you want to ensure mPulse runs in your development environment, you can make a small modification to your mPulse loader snippet.

Add this line before the rest of the snippet:

<script>
// add this line:
window.BOOMR_LOGN_always = true;

(function(){
    // ...
    // loader snippet above
    // ...
});
</script>

We do not recommend including this line in production code.

Using an Aliased Hostname

While mPulse will not run on an IP address or http://localhost, you can avoid this limitation by setting up a local DNS hostname alias that points at your local machine or any other IP address.

The easiest way to do this is to edit your hosts file. In the hosts file, you could add an alias for mymachine.domain.com 127.0.0.1 and it will look to the browser (and mPulse) like it is running on a real server.

On Unix, Linux and Mac OSX, you’ll want to edit /etc/hosts:

sudo nano /etc/hosts

On recent versions of Windows, you’ll want to edit %SystemRoot%\System32\drivers\etc\hosts. To do this, launch Notepad with Run As Administrator, and navigate to e.g. C:\Windows\System32\drivers\etc\hosts.

Once you have the file open in an editor, you’ll want to add a single line for your local machine (127.0.0.1) or the local IP address you’re using:

# Existing lines:
127.0.0.1    localhost
# ...

# Add this line:
127.0.0.1    mymachine.domain.com

Problems

Beacons Not Being Sent on Page Load

Boomerang sends beacons on page load to HTTP(s) endpoints with the pattern of *.mpstat.us or *.akstat.io.

To validate whether everything is working correctly:

  1. Validate whether the standard mPulse loader snippet is on the page.

    The loader snippet is required to load boomerang.js. By viewing the page’s source code, you should see the loader snippet JavaScript.

    If the loader snippet is not there, you will need to add it.

  2. Validate whether boomerang.js is loading via the browser developer tools.

    In the Networking tab, you should see a request to [c|s].go-mpulse.net/boomerang/[API-KEY].

    In the Source tab, you should see the minified boomerang.js source code.

    If you do not see boomerang.js in the Networking tab, check that the loader snippet is on the page.

  3. Validate whether config.js[on] is loading via the browser developer tools.

    config.js[on] contains app configuration and security tokens that are required for sending beacons.

    In the Networking tab, you should see a request to [c|s].go-mpulse.net/boomerang/config.js?key=[API-KEY].... or [c|s].go-mpulse.net/api/config.json?key=[API-KEY]....

    If you do not see config.js[on] in the Networking tab, check that boomerang.js has loaded, and validate there are no JavaScript errors from it in the Developer Tools Console.

  4. Validate whether beacons are being sent via the browser developer tools.

    In the Networking tab, you should see beacons being sent to a URL matching *.mpstat.us or *.akstat.io.

If you do not see beacons being sent, please review the following troubleshooting steps:

  1. Validate whether the Boomerang <IFRAME> in the <HEAD>.

    After the loader snippet executes, it will create an <IFRAME> in the <HEAD> of the HTML document. This <IFRAME> is used to load boomerang.js and config.js[on].

    This <IFRAME> must remain in the page for Boomerang to work.

    It will have a src of javascript:false, and a style of width: 0px; height: 0px; border: 0px; display: none.

    Within the <IFRAME>, it will have a <body onload="document._l()"> tag.

    Here is an example of a correct <IFRAME>:

    If the <IFRAME> is later removed, it will stop working, and it will not send beacons.

    Here is an example of what a removed <IFRAME> tag might look like. Note that the <body> was removed, even though the <IFRAME> is still there.

    If this has happened, there may be code on the website that is removing the <IFRAME> or modifying its contents.

API

General API documentation for Boomerang can be found at docs.soasta.com/boomerang-api/.

mPulse-specific documentation is below:

BOOMR.sendMetric(name, value)

The sendMetric() API sends a Custom Metric to mPulse.

The Custom Metric must already be defined in the mPulse app dialog.

Parameters

Example

// send an single metric
BOOMR.sendMetric("MyMetric", 2);

BOOMR.sendMetrics(metrics)

The sendMetrics() API sends multiple Custom Metrics to mPulse.

The Custom Metrics must already be defined in the mPulse app dialog.

Parameters

Example

// send multiple metrics at once
BOOMR.sendMetrics({
    "MyMetric": 2,
    "MyOtherMetric": 1
});

BOOMR.sendTimer(name, value)

The sendTimer() API sends a Custom Timer to mPulse.

The Custom Timer must already be defined in the mPulse app dialog.

Parameters

Example

// send a single timer
BOOMR.sendTimer("MyTime", 200);

BOOMR.sendTimers(timers)

The sendTimers() API sends multiple Custom Timers to mPulse.

The Custom Timers must already be defined in the mPulse app dialog.

Parameters

Example

// send multiple timers at once
BOOMR.sendTimers({
    "MyTime": 200,
    "MyOtherTime": 1000
});