Questions? Feedback? powered by Olark live chat software
Hopp over navigasjon

Case Study: Adding Application Insights to a website

Posted on 5 oktober, 2015

Program Manager, Application Insights

Our team helped to add Application Insights to a website, GetGlimpse.com. Glimpse is an open-source project, so we thought we’d share some of the decisions we made along the way. This post highlights common steps when onboarding into Application Insights and covers a couple of techniques that have not been documented before.

Glimpse developers want to make sure their website is reliable, fast and meets their users’ needs. They added Application Insights to their site to monitor its performance and usage in order to make more informed decisions as they prepare for their next release.

GetGlimpse.com is an ASP.NET MVC application. The site’s source code is on GitHub and deployed as an Azure Web App.

Process

We started with the standard procedure to onboard a web app to Application Insights. With the Glimpse site, we went a number of steps further:

  1. Read instrumentation key from Azure web app settings.
  2. Optimize JavaScript loading.
  3. Remove unnecessary modules.
  4. Get telemetry tagged with build version.
  5. Add full exception reporting.

After going through these steps there will be a summary and an overview of our findings.

Read instrumentation key from Azure Web App settings

The Application Insights instrumentation key directs the telemetry to the correct resource so that you can see your telemetry. Usually, you place it in the ApplicationInsights.config file and the application picks it up from there.

But in an Azure Web App, you can read the instrumentation key from the Azure Web App environment variables instead. We did this to allow overriding instrumentation key for different deployments. string ikeyValue = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY");

if (!string.IsNullOrEmpty(ikeyValue))
{
   TelemetryConfiguration.Active.InstrumentationKey = ikeyValue;
}

 
To make this work, you can add the Azure Application Insights extension to the web app, or set your instrumentation key in the website settings manually.

Optimize JavaScript loading

Client-side data collection requires JavaScript code to be inserted in the page. The standard advice is to put this code in the head of every web page on your site – usually by including it in a master page such as:

Views/Shared/_Layout.cshtml.

But this code can be rather large (almost 1KB). To reduce the overhead of downloading this for every page, we created a separate JavaScript file, allowing the browser to cache it across pages. This made an improvement in page load times.

Create a new JavaScript file to store the Application Insights snippet

Our script file  contains the code snippet you get from the Application Insights quick start/“Get code to monitor my web page” blade, but with one change near the end:

}({
    instrumentationKey: window.instrumentationKey
});

Instead of hard coding the instrumentation key, we replaced it with window.instrumentationKey. This keeps in line with our goal of hard coding our key in only one place.

Set instrumentation key from the server

Since ASP.NET web pages are generated at runtime, we can set the instrumentation key from the server SDK. Using the Razor syntax (you’d need to adapt this for other frameworks), we set the key in the _Layout (master) page:

 <script language="javascript">
        window.instrumentationKey = "@Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.Active.InstrumentationKey";
    </script>
    @Scripts.Render("~/bundles/headsitejs")

The Scripts.Render line uses the bundle feature in ASP.NET, which optimizes loading of multiple scripts.

Define a bundle for our script file

Finally, we added a new script bundle in AppStart/BundleConfig.cs to load our code file:

  bundles.Add(new ScriptBundle("~/bundles/headsitejs").Include(
                "~/Scripts/ApplicationInsightsSnippet.js"));

Benefit

Our goal was to have one place to set our Instrumentation Key. This makes updating the Instrumentation Key simple and clean. The benefit of reducing the size of pages by 1K may seem negligible, but for a larger website it adds up quickly. The combination of having Application Insights’ JavaScript code cached and the size of pages reduced is worth the setup.

Remove unnecessary modules

In the ApplicationInsights.config there is a telemetry initializer we don’t need for this app. This initializer is used with Azure cloud services, but our app is deployed as an Azure Web App:

<Add Type="Microsoft.ApplicationInsights.WindowsServer.AzureRoleEnvironmentTelemetryInitializer, Microsoft.AI.WindowsServer" /> 

We also removed the performance counters module, which doesn’t work yet in Azure Web Apps.

Both of these removals were aimed at improving performance.

Get telemetry tagged with the build version

The developers often deploy updates to the app. When they investigate exceptions or other telemetry, it sometimes isn’t clear which version the data comes from. But Application Insights makes it easy to add build version as a property to all telemetry, so they can check that property or filter.

We only have to get MSBuild to generate buildinfo.config. In our main csproj file we added: 
<PropertyGroup>
 ​<GenerateBuildInfoConfigFile>true</GenerateBuildInfoConfigFile>
 <IncludeServerNameInBuildInfo>true</IncludeServerNameInBuildInfo>
</PropertyGroup>

When it has the build info, the Application Insights web module automatically adds the build version as a property to every item of telemetry allowing us to filter by version when performing diagnostic searches or when exploring metrics.

Note that you will see the correct build number only when you use MSBuild to build your web app. Visual Studio’s local build process only puts a placeholder in the version property.

Add full exception reporting

To  have exceptions reported by Application Insights, some additional code is required in web apps. The techniques vary depending on the type of app. For Glimpse’s Web API we needed to add/modify four files to their project solution.

We defined an attribute in ApplicationInsightsErrorAttribute.cs. This reports exceptions in any class to which it is applied.

using System;
using System.Web.Mvc;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;

namespace Glimpse.Site.Telemetry
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public class ApplicationInsightsErrorAttribute : HandleErrorAttribute
    {
        private TelemetryClient client = new TelemetryClient();

        public override void OnException(ExceptionContext filterContext)
        {
            if (filterContext != null && filterContext.HttpContext != null && filterContext.Exception != null)
            {
                //If customError is Off, then AI HTTPModule will report the exception
                if (filterContext.HttpContext.IsCustomErrorEnabled)
                {
                    ExceptionTelemetry exc = new ExceptionTelemetry(filterContext.Exception);
                    exc.HandledAt = ExceptionHandledAt.Platform;
                    client.TrackException(exc);
                }
                base.OnException(filterContext);
            }
        }
    }
}

Note: Glimpse.site already had error handling. To work with their error handling we used HandleErrorAttribute instead of ExceptionFilterAttribute or ExceptionLogger as recommended in our documentation.

In FilterConfig.cs we added our Application Insights Error Attribute filter. This effectively applies the attribute to every class.

using System.Web.Mvc;
using Glimpse.Site.Telemetry;

namespace Glimpse.Site
{
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new ApplicationInsightsErrorAttribute());
        }
    }
}

In Glimpse.Site.csproj we included the new ApplicationInsightsErrorAttribute.cs file:

<Compile Include="Telemetry\ApplicationInsightsErrorAttribute.cs" />

Findings

Application Insights provided useful data to the developers of the Glimpse site. The overall finding was the Glimpse site is running well and most customers have a good experience with it. Glimpse developers were under the impression their site was stable and working well; Application Insights reassured this. It is important to note that the Glimpse site is constantly evolving with a large community and as such, there were some links on external sites to pages that no longer exist on the Glimpse site.

Because the Glimpse team was unaware of outdated links on other sites, they were surprised to see the number of errors Application Insights collected caused by broken links. These broken links reduced the quality of the experience for a small percentage of their customers. The majority of customers have a great experience with Glimpse site. However, to make the experience even better we made a triage of the exceptions that occurred the most. Here are some examples of exceptions we found:

File not found

System.IO.FileNotFoundException Could not find file 'D:\home\site\wwwroot\Views\Docs\Wiki\Content\Custom-Runtime-Policy.md'

This exception might seem scary, but detailed analysis showed it was just a broken link. The file was renamed, but someone tried to access the old documentation page.

Key not found exception

System.Collections.Generic.KeyNotFoundException

After investigating this exception, Glimpse developers discovered this exception may occur when a user upgrades their version of Glimpse. If upgrading through a PowerShell script the NuGet package might get confused about what version the user is going from/to. Because of this the NuGet package creates a query string that is unexpected by the server and therefore invalid.

Window.onerror

Error: Object doesn't support this property or method

After inspecting this error the Glimpse team found the issue arose because there is a version of IE8 on Windows Media Center that does not support window.onerror. The error was not common.

CORS script error

The browser’s same-origin policy prevents us from getting the details of this exception.The exception occurred in a script loaded from an origin different than the web page. For cross-domain error reporting you can use crossorigin attribute together with appropriate CORS HTTP headers. For more information please see http://www.w3.org/TR/cors/.

Sometimes JavaScript errors occur and because of security policies the errors are not very informative. The issue occurs because the script that causes an exception is hosted on a domain that differs from the domain the user experiences the exception on. Because of the security restrictions, Application Insights will not show any additional information for this kind of error. However, this exception was rare and happened in one script that is not a priority.

Adding Application Insights to the Glimpse site has equipped Glimpse developers with telemetry. They plan on using this data to improve site usability and performance. Overall they were very happy to find the majority of their users have flawless experiences with their site. Thanks to Nik Molnar and Anthony van der Hoorn who helped with this analysis

Summary

Onboarding Glimpse to Application Insights provided Glimpse with useful telemetry. Some of the techniques we used during this process are recommended and can contribute to an even richer Application Insights experience.