Capturing ASP.NET Framework RawUrl with Azure Application Insights
Niels Swimberghe - - Azure
Follow me on Twitter, buy me a coffee
Azure Application Insights is an Application Performance Management (APM) tool providing insights into the state of your application. By default, Application Insights will capture a lot of data about your ASP.NET applications including HTTP Requests made to your website. Unfortunately, the URL captured by Application Insights doesn't always match the URL originally requested by the client.
In many ASP.NET applications, especially CMS's like DNN, the path of the HTTP request is internally rewritten by the time Application Insights records the data. This results in the rewritten URL to be captured and not the original URL. You can simulate this scenario with a simple Context.RewritePath
inside of the Application_BeginRequest
method of the global.asax.cs
file:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; namespace AddRawUrlToApplicationInsights { public class MvcApplication : System.Web.HttpApplication { protected void Application_BeginRequest(object sender, EventArgs e) { // This code snippet is for demo purposes only! // these types of rewrites can be performed by IIS Rewrite module // or you can update the routing table to resolve '/' to the HomeController string fullOrigionalpath = Request.Url.ToString(); if (fullOrigionalpath.Contains("/about")) { Context.RewritePath("/home/about"); } else if(fullOrigionalpath.Contains("/contact")) { Context.RewritePath("/home/contact"); } } } }
In this sample, when /about
is requested, the path is rewritten to /home/about
which will resolve to the HomeController.About
action. When you browse to /about
and look at the recorded HTTP request inside of Application Insights, the URL will be https://domain.tld/home/about
instead of https://domain.tld/about
. The rewritten URL is useful information, but you may also need the original URL for proper debugging/analyzing.
Luckily, there's a property called RawUrl
on the request that will always have the original path including the querystring. Application Insights provides an extensibility point for you to capture additional data using the ITelemetryInitializer
interface. You can capture the original URL by implementing ITelemetryInitializer
and adding the RawUrl
data to the telemetry properties:
using System; using System.Web; using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; namespace AddRawUrlToApplicationInsights { public class RawUrlTelemetryInitializer : ITelemetryInitializer { public void Initialize(ITelemetry telemetry) { if (telemetry is RequestTelemetry requestTelemetry) { var httpContext = HttpContext.Current; if (httpContext?.Request == null) { return; } var request = httpContext.Request; requestTelemetry.Properties["RawUrl"] = request.RawUrl; requestTelemetry.Properties["RawUrlFqdn"] = new Uri(request.Url, request.RawUrl).ToString(); } } } }
Whenever an HTTP Request telemetry is initialized, this initializer will grab the current HTTP Request and capture the RawUrl
and the RawUrlFqdn
. The RawUrlFqdn
will include the protocol, full domain, port, path, and querystring.
For Application Insights to use the RawUrlTelemetryInitializer
, you must add a reference to the class in ApplicationInsights.config
:
<?xml version="1.0" encoding="utf-8"?> <ApplicationInsights xmlns="http://schemas.microsoft.com/ApplicationInsights/2013/Settings"> <TelemetryInitializers> <Add Type="AddRawUrlToApplicationInsights.RawUrlTelemetryInitializer, AddRawUrlToApplicationInsights"/> ... </TelemetryInitializers> ... </ApplicationInsights>
This is what the resulting data looks like when sent to Application Insights:
{ "name": "AppRequests", "time": "2020-09-11T16:47:04.4346673Z", "tags": { "ai.cloud.roleInstance": "YOURHOSTNAME", "ai.operation.id": "26d253e8979c0f4fb602fc7df4a31039", "ai.operation.name": "GET home/about", "ai.location.ip": "::1", "ai.internal.sdkVersion": "web:2.14.0-17971" }, "data": { "baseType": "RequestData", "baseData": { "ver": 2, "id": "59b220044848c141", "name": "GET home/about", "duration": "00:00:01.6182094", "success": true, "responseCode": "200", "url": "https://localhost:44308/home/about", "properties": { "RawUrlFqdn": "https://localhost:44308/about", "RawUrl": "/about", "DeveloperMode": "true", "_MS.ProcessedByMetricExtractors": "(Name:'Requests', Ver:'1.1')" } } } }
Notice how the url
, properties.Rawurl
, and properties.RawUrlFqdn
are all capture providing a more complete picture of the request:
url | https://localhost:44308/home/about |
properties.RawUrlFqdn | https://localhost:44308/about |
properties.RawUrl | /about |
Summary #
Using the ITelemetryInitializer
extensibility point in Application Insights, you can capture additional data. Using the RawUrl
on the HttpContext.Request
, you can capture the original URL in addition to the rewritten URL which is captured by default.