Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using Example.Extensions;
namespace Example.Filters
{
/// <summary>
/// It turns out that request grouping in Application Insights is broken by default when using attribute-based
/// routing, since identifiers are included in request names. This filter fixes that by using the route templates
/// instead of the URLs. It will not work for requests that never reach the action filter chain however (such as
/// requests rejected by the version handler or auth filters).
/// </summary>
// See: http://apmtips.com/blog/2017/01/12/update-to-the-last-post-set-the-name-in-mvc-web-api/
public class ApplicationInsightsRequestGroupingFilter : IActionFilter
{
public Task<HttpResponseMessage> ExecuteActionFilterAsync(
HttpActionContext actionContext,
CancellationToken cancellationToken,
Func<Task<HttpResponseMessage>> continuation)
{
var routeData = actionContext.RequestContext.RouteData;
var template = routeData.Route.RouteTemplate;
if (template != null && template.Length == 0)
{
// Root of web service
template = "/";
}
var requestTelemetry = actionContext.Request.GetHttpContext().GetRequestTelemetry();
// Overwrite name in request telemetry to fix grouping of requests in Application Insights
if (template != null && requestTelemetry != null)
{
requestTelemetry.Name = template;
requestTelemetry.Context.Operation.Name = template;
}
return continuation();
}
public bool AllowMultiple
{
get { return false; }
}
}
}
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Web;
using Microsoft.ApplicationInsights.DataContracts;
namespace Example.Extensions
{
public static class HttpRequestMessageExtensions
{
private const string HttpContextKey = "MS_HttpContext";
public static HttpContextBase GetHttpContext(this HttpRequestMessage request)
{
HttpContextBase context = null;
object requestValue;
request.Properties.TryGetValue(HttpContextKey, out requestValue);
context = requestValue as HttpContextBase;
if (context == null)
{
PrintDiagnostics(typeof(HttpContextBase), typeof(HttpRequestMessage), HttpContextKey, requestValue);
}
return context;
}
/// <summary>Diagnostics: figure out why we could not access context</summary>
internal static void PrintDiagnostics(Type typeOfDesiredObject, Type typeOfContainer, string keyOfDesiredObjectInContainer, object valueAtKey)
{
string msg;
if (valueAtKey == null)
{
msg = String.Format("Could not access {0}: no value present in {1} for key {2}",
typeOfDesiredObject, typeOfContainer, keyOfDesiredObjectInContainer);
}
else
{
msg = String.Format("Could not access {0}: expected value in {1} for key {2} to be of type {0} but was of type {3}",
typeOfDesiredObject, typeOfContainer, keyOfDesiredObjectInContainer, valueAtKey.GetType().FullName);
}
Trace.WriteLine(msg);
}
}
public static class HttpContextExtensions
{
// The AI SDK includes a GetRequestTelemetry extension method so we don't have to know this key, unfortunately
// it extends HttpContext and not HttpContextBase/HttpContextWrapper and we cannot access the context inside
// the wrapper...
// See: https://github.com/Microsoft/ApplicationInsights-dotnet-server/issues/294
private const string RequestTelemetryKey = "Microsoft.ApplicationInsights.RequestTelemetry";
public static RequestTelemetry GetRequestTelemetry(this HttpContextBase context)
{
RequestTelemetry telemetry = null;
object contextValue = null;
if (context != null)
{
contextValue = context.Items[RequestTelemetryKey];
telemetry = contextValue as RequestTelemetry;
}
if (telemetry == null)
{
HttpRequestMessageExtensions.PrintDiagnostics(typeof(RequestTelemetry), typeof(HttpContextBase), RequestTelemetryKey, contextValue);
}
return telemetry;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment