Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -244,12 +244,12 @@ public void OnStopActivity(Activity activity, object? payload)
var response = context.Response;

#if !NETSTANDARD
var routePattern = (context.Features.Get<IExceptionHandlerPathFeature>()?.Endpoint as RouteEndpoint ??
context.GetEndpoint() as RouteEndpoint)?.RoutePattern.RawText;
if (!string.IsNullOrEmpty(routePattern))
var endpoint = context.Features.Get<IExceptionHandlerPathFeature>()?.Endpoint as RouteEndpoint
?? context.GetEndpoint() as RouteEndpoint;

if (endpoint != null)
{
TelemetryHelper.RequestDataHelper.SetActivityDisplayName(activity, context.Request.Method, routePattern);
activity.SetTag(SemanticConventions.AttributeHttpRoute, routePattern);
activity.SetRouteAttributeTag(endpoint, context.Request);
}
#endif

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,12 @@ public static void OnStopEventWritten(string name, object? payload)

#if NET
// Check the exception handler feature first in case the endpoint was overwritten
var route = (context.Features.Get<IExceptionHandlerPathFeature>()?.Endpoint as RouteEndpoint ??
context.GetEndpoint() as RouteEndpoint)?.RoutePattern.RawText;
if (!string.IsNullOrEmpty(route))
var endpoint = context.Features.Get<IExceptionHandlerPathFeature>()?.Endpoint as RouteEndpoint
?? context.GetEndpoint() as RouteEndpoint;

if (endpoint != null)
{
tags.Add(new KeyValuePair<string, object?>(SemanticConventions.AttributeHttpRoute, route));
tags.AddRouteAttribute(endpoint, context.Request);
}
#endif
if (context.Items.TryGetValue(ErrorTypeHttpContextItemsKey, out var errorType))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#if !NETSTANDARD

using System.Diagnostics;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Patterns;
using OpenTelemetry.Trace;

namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation;

internal static class RouteAttributeHelper
{
public static void AddRouteAttribute(this TagList tags, RouteEndpoint endpoint, HttpRequest request)
{
var routePattern = GetRoutePattern(endpoint.RoutePattern, request.RouteValues);

if (!string.IsNullOrEmpty(routePattern))
{
tags.Add(new KeyValuePair<string, object?>(SemanticConventions.AttributeHttpRoute, routePattern));
}
}

public static void SetRouteAttributeTag(this Activity activity, RouteEndpoint endpoint, HttpRequest request)
{
var routePattern = GetRoutePattern(endpoint.RoutePattern, request.RouteValues);

if (!string.IsNullOrEmpty(routePattern))
{
TelemetryHelper.RequestDataHelper.SetActivityDisplayName(activity, request.Method, routePattern);
activity.SetTag(SemanticConventions.AttributeHttpRoute, routePattern);
}
}

private static string GetRoutePattern(RoutePattern routePattern, RouteValueDictionary routeValues)
{
if (routePattern.PathSegments.Count == 0)
{
// RazorPage default route
if (routePattern.Defaults.TryGetValue("page", out var pageValue))
{
return pageValue?.ToString()?.Trim('/')
?? string.Empty;
}

return string.Empty;
}

var sb = new StringBuilder();

foreach (var segment in routePattern.PathSegments)
{
foreach (var part in segment.Parts)
{
if (part is RoutePatternLiteralPart literalPart)
{
sb.Append(literalPart.Content);
sb.Append('/');
}
else if (part is RoutePatternParameterPart parameterPart)
{
switch (parameterPart.Name)
{
case "area":
case "controller":
case "action":
if (routePattern.RequiredValues.TryGetValue(parameterPart.Name, out var parameterValue) && parameterValue != null)
{
sb.Append(parameterValue);
sb.Append('/');
break;
}

goto default;
default:
if (!parameterPart.IsOptional ||
(parameterPart.IsOptional && routeValues.ContainsKey(parameterPart.Name)))
{
sb.Append('{');
sb.Append(parameterPart.Name);
sb.Append('}');
sb.Append('/');
}

break;
}
}
}
}

// Remove the trailing '/'
return sb.ToString(0, sb.Length - 1);
}
}

#endif
Loading