Technical Overview: URL Parameter Capture with AppDynamics RUM for AppDynamics Analytics

Gathering metrics on how users interact with your website and application frontends is an essential factor in today’s monitoring world. Organizations must gather and analyze more specific user metrics. In this post, I will discuss using AppDynamics Analytics and AppDynamics Browser Real User Monitoring (BRUM) to extract URL parameters from a user’s browser to analyze and filter in AppDynamics Analytics. Before we get to the specifics, let me give a brief introduction to BRUM and Analytics.

BRUM:

Browser Real-User Monitoring (Browser RUM) is an AppDynamics component that allows you to see how your web application works from the point of view of the real end-user. You can drill into the data to explore how users experience your application in their web browsers.

Analytics:

Analytics is another component utilized by the AppDynamics APM platform. Analytics can answer business-oriented questions such as:

  • How many users experienced failed checkout transactions in the last 24 hours?
  • How much revenue was lost because of these failures?
  • How is the lost revenue distributed across different product categories?
  • What is your revenue for the day for a geographical region?
  • What was the revenue impact, by product category, associated with the two marketing campaigns we ran last week?

BRUM + Analytics:

AppDynamics Application Analytics has a component called Browser Analytics, which  is based on the same Events Service and uses the same data gathering method and BRUM, but it offers additional capabilities, including:

  • Additional predefined widgets, such as the funnel widget
  • ADQL for searching the data
  • Creating custom widgets
  • Manipulating multiple dashboard types
  • Longer retention time for data storage

Out of the box, AppDynamics does not offer a solution to extract URL parameters and send them to analytics for manipulation into meaningful dashboards and searches. Evolving Solutions, with the help of our partners, has developed a solution to gather URL parameters as necessary for our client. I will explain how that is accomplished and the reasons why it is useful.

The Details

Recently, a Fortune 500 company came to Evolving Solutions with a request to pull a URL parameter (store-id) from a user’s session for the purpose of sorting and analyzing data, and diagnosing problems, based on a specific “store.” Normally, in End User Monitoring (EUM), data such as store location is aggregated together in the URL and is not easily searchable/defined. To meet the request, we modified the configuration of the AppDynamics JavaScript agent to capture the URL the user accessed, and we parsed the parameter we wanted. For reference, here is the result as we go through the steps:

<script charset="UTF-8" type="text/javascript">

    window['adrum-start-time'] = new Date().getTime();

    (function(config) {

        config.appKey = window['adrum-app-key'];

        config.adrumExtUrlHttp = 'http://ENV_APPDYNAMICS_CDN_URL';

        config.adrumExtUrlHttps = 'https://ENV_APPDYNAMICS_CDN_URL';

        config.beaconUrlHttp = 'http://ENV_APPDYNAMICS_DEACON_URL';

        config.beaconUrlHttps = 'https://ENV_APPDYNAMICS_DEACON_URL';

        config.xd = {

            enable: false

        };

        config.page = {

            captureTitle: true

        };

        config.resTiming = {

            bufSize: 200,

            clearResTimingOnBeaconSend: true

        };

        config.maxUrlLength = 512;

        config.fetch = true;

        config.spa = {

            spa2: true

        };

        config.isZonePromise = true;

        config.angular = true;

        (function(info) {

            info.Ajax = function(context) {

                return {

                    userData: extractUserData(context)

                }

            };

            info.PageView = function(context) {

                return {

                    userData: extractUserData(context)

                }

            };

        })(config.userEventInfo || (config.userEventInfo = {}))

    })(window['adrum-config'] || (window['adrum-config'] = {}));</script>




function extractUserData(context) {

    var sID = null;

    if (document.URL.includes("store-id=")) {

        var start = document.URL.indexOf("store-id=");

        SID = document.URL.substr(start + 10, 36);

    } else {

        sID = "store ID not available";

    }

    return {

        storeId: sID

    };

}

<script src="//cdn.appdynamics.com/adrum/adrum-latest.js"></script>

This is the full configuration file (data expunged) for our SPA. The parameter we are targeting is “store-id”, but first we need to extract the userData to be able to grab the URL. (We are using a SPA, single page application, built on Ajax for this blog.)

URL: https://customer.net/customer.web/home?store-id=70d64ccd-a714-4eb8-a114-00e296f4f1db

(function(info) {

            info.Ajax = function(context) {

                return {

                    userData: extractUserData(context)

                }

            };

            info.PageView = function(context) {

                return {

                    userData: extractUserData(context)

                }

            };

        })

In the code above we are grabbed the user context values and extracted them from the user. For more information on the info, context, extractUserData, and userData parameters see this link. Once we capture this data, we can use it to:

1. Identify what URLs have “store-id” in it
2. Extract the “store-id” from the URL
3. Return it to AppDynamics as a custom user data field

These tasks can be accomplished in two ways, depending on how your URL parameter functions:

First, if have a URL that always ends with the parameter in question, we can add this function to the JavaScript configuration.

function extractUserData(context) {

    var sID = null;

    if (document.URL.includes("store-id=")) {

        var start = document.URL.indexOf("store-id=");

        sID = document.URL.substr(start);

    } else {

        sID = "store ID not available";

    }

    return {

        storeId: sID

    };

}

Above we instantiate the variable sID.

var sID = null;

Ensure that the URL contains “store-id”

 if (document.URL.includes("store-id=")

Take the indexOf “store-id=” as a starting point for the substr() Method and then use that to grab everything after “store-id”

var start = document.URL.indexOf("store-id=");

sID = document.URL.substr(start);

After that, you need an “else” statement to filter out the URLs without “store-id” and a return.

What if you don’t want to capture any of the string after the parameter? The example below is what we used due to dynamic changes after the parameter.

URL: https://customer.net/customer.web/home?store-id=70d64ccd-a714-4eb8-a114-00e296f4f1db/

For the URL above, our code would return “70d64ccd-a714-4eb8-a114-00e296f4f1db/” but we don’t want to capture the “” part, so let’s clean it up:

function extractUserData(context) {

    var sID = null;

    if (document.URL.includes("store-id=")) {

        var start = document.URL.indexOf("store-id=");

        SID = document.URL.substr(start + 10, 36);

    } else {

        sID = "store ID not available";

    }

    return {

        storeId: sID

    };

}

What we did was an exchange:

sID = document.URL.substr(start);

for

SID = document.URL.substr(start + 10, 36);

This exchange ensures we only capture the section that contains our parameter.

Again, the complete product:

<script charset="UTF-8" type="text/javascript">

    window['adrum-start-time'] = new Date().getTime();

    (function(config) {

        config.appKey = window['adrum-app-key'];

        config.adrumExtUrlHttp = 'http://ENV_APPDYNAMICS_CDN_URL';

        config.adrumExtUrlHttps = 'https://ENV_APPDYNAMICS_CDN_URL';

        config.beaconUrlHttp = 'http://ENV_APPDYNAMICS_DEACON_URL';

        config.beaconUrlHttps = 'https://ENV_APPDYNAMICS_DEACON_URL';

        config.xd = {

            enable: false

        };

        config.page = {

            captureTitle: true

        };

        config.resTiming = {

            bufSize: 200,

            clearResTimingOnBeaconSend: true

        };

        config.maxUrlLength = 512;

        config.fetch = true;

        config.spa = {

            spa2: true

        };

        config.isZonePromise = true;

        config.angular = true;

        (function(info) {

            info.Ajax = function(context) {

                return {

                    userData: extractUserData(context)

                }

            };

            info.PageView = function(context) {

                return {

                    userData: extractUserData(context)

                }

            };

        })(config.userEventInfo || (config.userEventInfo = {}))

    })(window['adrum-config'] || (window['adrum-config'] = {}));</script>




function extractUserData(context) {

    var sID = null;

    if (document.URL.includes("store-id=")) {

        var start = document.URL.indexOf("store-id=");

        SID = document.URL.substr(start + 10, 36);

    } else {

        sID = "store ID not available";

    }

    return {

        storeId: sID

    };

}

<script src="//cdn.appdynamics.com/adrum/adrum-latest.js"></script>

Conclusion
There are many ways the little snippet of code below could be modified for different use cases, from dynamic parameter sizes to grabbing specific sections of the URL that are not parameters. Hopefully, this blog gets you started or generates ideas for use at your company. Reach out to [email protected] with any questions/comments/inquiries about this blog or the services we offer to our clients.

function extractUserData(context) {
var sID = null;
if (document.URL.includes("store-id=")) {
var start = document.URL.indexOf("store-id=");
SID = document.URL.substr(start + 10, 36);
} else {
sID = "store ID not available";
}
return {
storeId: sID
};

Getting Started with AppDynamics
If you don’t have an AppDynamics account, you can obtain a free trial. Feel free to contact us at [email protected] and our team can assist you with this process and to setup an education plan for your team.

Author: Naveen Morisetti

Michael Downs

Chief Technology Officer

Michael is the Chief Technology Officer at Evolving Solutions and joined the company in 2014. Connect with Michael on LinkedIn here.

Photo of Michael Downs

Related Blog Posts