Created
November 20, 2021 19:11
-
-
Save angularsen/551bcbc5f770d85ff9c4dfbab4465546 to your computer and use it in GitHub Desktop.
Simple request telemetry names
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#nullable enable | |
using System; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Mvc; | |
using Microsoft.AspNetCore.Mvc.Filters; | |
namespace Digma.Api.Common.Telemetry | |
{ | |
/// <summary> | |
/// Action filter to construct a simpler telemetry name from the route name or the route template. | |
/// <br/><br/> | |
/// Out of the box, Application Insights sometimes uses request telemetry names like "GET /chat/ba1ce6bb-01e8-4633-918b-08d9a363a631/since/2021-11-18T18:51:08". | |
/// This makes it hard to see how many requests were for a particular API action. | |
/// This is a <a href="https://github.com/microsoft/ApplicationInsights-dotnet/issues/1418">known issue</a>. | |
/// <br/><br/> | |
/// - If route name is defined, then use that.<br/> | |
/// - If route template is defined, then the name is formatted as "{method} /{template} v{version}". | |
/// </summary> | |
/// <example> | |
/// - <b>"MyCustomName"</b> if route name is explicitly defined with <c>[Route("my_path", Name="MyCustomName")]</c><br/> | |
/// - <b>"GET /config v2.0"</b> if template is "config" and API version is 2.0.<br/> | |
/// - <b>"GET /config"</b> if no API version is defined. | |
/// </example> | |
/// <remarks> | |
/// The value is passed on via <see cref="HttpContext.Items"/> with the key <see cref="SimpleRequestTelemetryNameInitializer.TelemetryNameKey"/>. | |
/// </remarks> | |
public class SimpleRequestTelemetryNameActionFilter : ActionFilterAttribute | |
{ | |
public override void OnActionExecuting(ActionExecutingContext context) | |
{ | |
var httpContext = context.HttpContext; | |
var attributeRouteInfo = context.ActionDescriptor.AttributeRouteInfo; | |
if (attributeRouteInfo?.Name is { } name) | |
{ | |
// If route name is defined, it takes precedence. | |
httpContext.Items.Add(SimpleRequestTelemetryNameInitializer.TelemetryNameKey, name); | |
} | |
else if (attributeRouteInfo?.Template is { } template) | |
{ | |
// Otherwise, use the route template if defined. | |
string method = httpContext.Request.Method; | |
string versionSuffix = GetVersionSuffix(httpContext); | |
httpContext.Items.Add(SimpleRequestTelemetryNameInitializer.TelemetryNameKey, $"{method} /{template}{versionSuffix}"); | |
} | |
base.OnActionExecuting(context); | |
} | |
private static string GetVersionSuffix(HttpContext httpContext) | |
{ | |
try | |
{ | |
var requestedApiVersion = httpContext.GetRequestedApiVersion()?.ToString(); | |
// Add leading whitespace so we can simply append version string to telemetry name. | |
if (!string.IsNullOrWhiteSpace(requestedApiVersion)) | |
return $" v{requestedApiVersion}"; | |
} | |
catch (Exception) | |
{ | |
// Some requests lack the IApiVersioningFeature, like requests to get swagger doc | |
} | |
return string.Empty; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.ApplicationInsights.Channel; | |
using Microsoft.ApplicationInsights.DataContracts; | |
using Microsoft.ApplicationInsights.Extensibility; | |
using Microsoft.AspNetCore.Http; | |
namespace Digma.Api.Common.Telemetry | |
{ | |
/// <summary> | |
/// Changes the name of request telemetry to the value assigned by <see cref="SimpleRequestTelemetryNameActionFilter"/>. | |
/// </summary> | |
/// <remarks> | |
/// The value is passed on via <see cref="HttpContext.Items"/> with the key <see cref="TelemetryNameKey"/>. | |
/// </remarks> | |
public class SimpleRequestTelemetryNameInitializer : ITelemetryInitializer | |
{ | |
internal const string TelemetryNameKey = "SimpleOperationNameInitializer:TelemetryName"; | |
private readonly IHttpContextAccessor _httpContextAccessor; | |
public SimpleRequestTelemetryNameInitializer(IHttpContextAccessor httpContextAccessor) | |
{ | |
_httpContextAccessor = httpContextAccessor; | |
} | |
public void Initialize(ITelemetry telemetry) | |
{ | |
var httpContext = _httpContextAccessor.HttpContext; | |
if (telemetry is RequestTelemetry requestTelemetry && httpContext != null) | |
{ | |
if (httpContext.Items.TryGetValue(TelemetryNameKey, out var telemetryNameObj) | |
&& telemetryNameObj is string telemetryName | |
&& !string.IsNullOrEmpty(telemetryName)) | |
{ | |
requestTelemetry.Name = telemetryName; | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Foo | |
{ | |
public class Startup | |
{ | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
// Register telemetry initializer. | |
services.AddApplicationInsightsTelemetry(); | |
services.AddSingleton<ITelemetryInitializer, SimpleRequestTelemetryNameInitializer>(); | |
services.AddMvc(opt => | |
{ | |
// Global MVC filters. | |
opt.Filters.Add<SimpleRequestTelemetryNameActionFilter>(); | |
}); | |
} | |
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) | |
{ | |
// ...other configuration | |
} | |
} | |
} |
@jigarce That is a just a unique string to store the value in HttpContext.Items
, you don't need to change it.
I can't see any missing pieces here although I had to adapt from our code base.
Make sure you register everything as described in Startup.cs example.
I think you just have to debug a bit:
- Is
SimpleRequestTelemetryNameActionFilter.OnActionExecuting()
run as part of your API request? Does it compute the simplified name and store it inHttpContext.Items
? - Is
SimpleRequestTelemetryNameInitializer.Initialize()
run after the request? What values does it get?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@angularsen Can you share proper example. I have tried implementing your code but it is not working.
what should I write in place of "SimpleOperationNameInitializer:TelemetryName" ? its Name or key ?