Application Insights: Exception Telemetry

Application Insights lets you monitor your live application for its availability, performance and usage. Exception telemetry is of course, a central piece of this. In this blog, we’ll look at the compelling diagnostics experience enabled by capturing exceptions along with request telemetry. I’ll also illustrate how you can configure your application so as to make sure you capture all the exceptions for failed requests, in any .NET application (Framework 4.0+). There are some situations where Application Insights can’t automatically capture all the exceptions you might like to see because they are cleared before they get to our HTTPModule. But I’ll show you how to use our SDK to send all the unhandled exceptions, plus any exceptions that are handled in your application but that you’d still like to have reported.

For more information on how to get started with Application Insights and the SDK, please see the following:

Rich diagnostics with exceptions

Capturing exceptions along with request and page-view telemetry is a powerful diagnostic experience, especially since it’s possible to get handled and unhandled exceptions from both client and server.

The good experience starts on the Overview screen, where you can see at a glance when failed requests are correlated with server or browser exceptions, and click through to get a breakdown of specific exceptions:

basic

Drill into any exception to see its occurrences, and then click further to see what requests were affected by it:

Alan-top

Or instead, you can start from a failed request and get to the exceptions associated with it:

Alan2

From the exception reports you can get a stack listing and see what’s going on. To learn more about reading and searching the exception data, see Diagnostic search in Application Insights.

This is clearly a powerful diagnostic experience, and to start getting it you only have to add Application Insights to your project in Visual Studio, or install Status Monitor on your server. To collect browser-side data, you add a script to your master page, though this is done for you when you create a new web project.

Those tools will get you all the request telemetry and browser exceptions, and some of the server side exceptions. But in many cases, there’s a bit more you have to do to get all the server-side exceptions. It’s slightly different for each technology, and that’s what the rest of this blog is about.

How can I capture Exception Telemetry?

You don’t usually have to load them yourself, but it’s helpful to be aware that the Application Insights tools load the following packages:

The Application Insights HTTPModule captures server side Request telemetry, and it also captures any unhandled exceptions when the HTTPResponse has status code 400 and above. Unfortunately, in most project types and depending on the CustomErrors configuration, the exception information might have been cleared lower in the stack, so that our HTTPModule doesn’t see it. But you can fix this so that all unhandled exceptions are reported. You just have to write a few lines of code using the TrackException API. (You can of course use the same API to send data from your own exception handlers.) We will go over how you can collect unhandled exceptions for the following application types in the sections below.

  • MVC
  • Web API 1.x
  • Web API 2.x
  • WCF
  • Web Forms/Web Pages

finalNote

MVC

If the CustomErrors configuration is Off, then exceptions will be available for the HTTPModule to collect. However, if it is RemoteOnly (default), or On – then the exception will be cleared and not available for us to automatically collect. You can fix that by overriding the System.web.mvc.HandleErrorAttribute class, and applying the overridden class as shown for the different MVC versions below (github source):

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

namespace MVC2App.Controllers
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AiHandleErrorAttribute : HandleErrorAttribute
{
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)
{ //or reuse instance (recommended!). see note above
var ai = new TelemetryClient();

ai.TrackException(filterContext.Exception);
}
}
base.OnException(filterContext);
}
}
}

 

MVC 2: Replace HandleError attribute with the overridden attribute in your controllers as shown below (github source).

handleerrorAttribute

  • A sample MVC2 application wired up for exception collection is available here

MVC 3: Register AiHandleErrorAttribute as a global filter in Global.asax.cs as shown below (github source)

mvc3

  • A sample MVC3 application wired up for exception collection is available here

MVC 4/5: Register AiHandleErrorAttribute as a global filter in FilterConfig.cs as shown below (github source)

mvc5

  • A sample MVC5 application wired up for exception collection is available here

 

Web API 1.x

using System.Web.Http.Filters;
using Microsoft.ApplicationInsights;

namespace WebAPI.App_Start
{
public class AiExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext != null && actionExecutedContext.Exception != null)
{ //or reuse instance (recommended!). see note above
var ai = new TelemetryClient();
ai.TrackException(actionExecutedContext.Exception);

}
base.OnException(actionExecutedContext);
}
}

}
  • You could add this overridden attribute to specific controllers, or add it to the global filter configuration in the WebApiConfig class as shown below (github source):
using System.Web.Http;
using WebApi1.x.App_Start;

namespace WebApi1.x
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
//config.EnableQuerySupport();

// To disable tracing in your application, please comment out or remove the following line of code
// For more information, refer to: http://www.asp.net/web-api
config.EnableSystemDiagnosticsTracing();
config.Filters.Add(new AiExceptionFilterAttribute());
}
}
}
  • A sample Web API 1.x application wired up for exception collection is available here
  • Note: There are a number of cases that exception filters cannot handle. For example:
    • Exceptions thrown from controller constructors.
    • Exceptions thrown from message handlers.
    • Exceptions thrown during routing.
    • Exceptions thrown during response content serialization.

 

Web API 2.x

  • Note that the CustomErrors configuration only influences the exceptions from MVC controllers. To capture exceptions from MVC controllers see the section on MVC above.
  • To capture unhandled exceptions from Web API 2.x controllers, pick any one of 3 options:
    1. Add an implementation of IExceptionLogger: we recommend this approach as it is guaranteed to be called
    2. Replace the only ExceptionHandler with a custom implementation of IExceptionHandler. This is only called when the framework is still able to choose which response message to send (not when the connection is aborted for instance)
    3. Exception Filters (as described in the section on Web API 1.x controllers above)

Option 1 (Recommended): Implement System.Web.Http.ExceptionHandling.IExceptionLogger (github source)

using System.Web.Http.ExceptionHandling;
using Microsoft.ApplicationInsights;

namespace ProductsAppPureWebAPI.App_Start
{
public class AiExceptionLogger : ExceptionLogger
{
public override void Log(ExceptionLoggerContext context)
{
if (context !=null && context.Exception != null)
{//or reuse instance (recommended!). see note above
var ai = new TelemetryClient();
ai.TrackException(context.Exception);
}
base.Log(context);
}
}
}
  • Add this to the services, in the WebApiConfig class as shown below:
using System.Web.Http;
using System.Web.Http.ExceptionHandling;
using ProductsAppPureWebAPI.App_Start;

namespace WebApi2WithMVC
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services

// Web API routes
config.MapHttpAttributeRoutes();

config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Services.Add(typeof(IExceptionLogger), new AiExceptionLogger());
}
}
}

Option 2: Implement/Add to an existing implementation of System.Web.Http.ExceptionHandling.IExceptionHandler (github source)

  • Note: Only 1 ExceptionHandler can be registered. If you are using an existing handler, add to it, or extend it
using System.Web.Http.ExceptionHandling;
using Microsoft.ApplicationInsights;

namespace ProductsAppPureWebAPI.App_Start
{
public class AiExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
if (context != null && context.Exception != null)
{//or reuse instance (recommended!). see note above
var ai = new TelemetryClient();

ai.TrackException(context.Exception);

}
base.Handle(context);
}
}
}
  • Replace the ExceptionHandler registered in WebApiConfig class (github source)

webapi2

  • A sample Web API 2.x application wired up for exception collection is available here 

WCF

  • Add a class that extends Attribute and implements IErrorHandler and IServiceBehavior as shown below (github source)
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Web;
using Microsoft.ApplicationInsights;

namespace WcfService4.ErrorHandling
{
public class AiLogExceptionAttribute : Attribute, IErrorHandler, IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription,
System.ServiceModel.ServiceHostBase serviceHostBase,
System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints,
System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}

public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
System.ServiceModel.ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher disp in serviceHostBase.ChannelDispatchers)
{
disp.ErrorHandlers.Add(this);
}
}

public void Validate(ServiceDescription serviceDescription,
System.ServiceModel.ServiceHostBase serviceHostBase)
{
}

bool IErrorHandler.HandleError(Exception error)
{//or reuse instance (recommended!). see note above
var ai = new TelemetryClient();

ai.TrackException(error);

return false;
}

void IErrorHandler.ProvideFault(Exception error,
System.ServiceModel.Channels.MessageVersion version,
ref System.ServiceModel.Channels.Message fault)
{
}
}
}
  • Add the attribute to the service implementations as shown below:

WCF

  • A sample WCF application wired up for exception collection is available here

Web Forms/Web Pages

  • For Web forms/pages, the HTTPModule will be able to collect the Exceptions when there are no redirects configured with CustomErrors
  • If you have eligible redirects, then for Web Forms – add the following lines to the Application_Error function in Global.asax.cs:
void Application_Error(object sender, EventArgs e)
{
if (HttpContext.Current.IsCustomErrorEnabled && Server.GetLastError() != null)
{//or reuse instance (recommended!). see note above
var ai = new TelemetryClient();


ai.TrackException(Server.GetLastError());
}

}
  • For Web sites, add a Global.asax file (if you don’t already have one), and add the same lines of code as above for Web Forms into the Application_Error function.

Summary

As this article shows for different project types, a few lines of code can collect all unhandled, and specific handled exceptions (applications with Framework 4.0+) to complement the other Application Insights telemetry. This enables some compelling diagnostics scenarios. We are working on continually improving our automatic telemetry collection and enriching the SDK surface area to give you the necessary controls as well. Please give this a run! Looking forward to your feedback to help us improve and be effective in keeping your applications available, performing and successful with your users!