opentelemetry-dotnet icon indicating copy to clipboard operation
opentelemetry-dotnet copied to clipboard

Add support for endpoint routing in route template detection

Open tillig opened this issue 2 years ago • 1 comments

Feature Request

For a while now (since ~3.0) ASP.NET Core has been pretty flexible about how it allows you to define routes/patterns, much of which boils down internally to endpoint routing. For example, that's what the default WebApplicationBuilder does and it's how attribute routing (and other routing) gets added to the route table.

Currently the HttpInListener specifically gets the attribute route info from the current request and uses that to set the display name and route information. While this works fine for attribute routing, it misses cases like...

app
  .UseRouting()
  .UseEndpoints(endpoints =>
  {
    endpoints.MapControllers();
    endpoints.Map("/demo/{id}", ctx =>
    {
      ctx.Response.StatusCode = 200;
      return ctx.Response.WriteAsJsonAsync("missed");
    });
  });

The way to get routes out for endpoint routing is to get the IEndpointFeature out of the current request context:

if (context.Features.Get<IEndpointFeature>()?.Endpoint is RouteEndpoint endpoint)
{
  // endpoint.RoutePattern is the pattern - from attribute or otherwise
  // endpoint.RoutePattern.RawText is probably what the activity DisplayName should be
}
else
{
  // Fallback to path or something that isn't route pattern.
}

I noticed in the code that the MVC DiagnosticListener setup was referenced (which has actually moved to a new location since the old MVC repo is deprecated) and the current context does look like it's attached to the event so it should be available.

I also noticed the code mentioned...

// Reflection accessing: ActionDescriptor.AttributeRouteInfo.Template
// The reason to use reflection is to avoid a reference on MVC package.
// This package can be used with non-MVC apps and this logic simply wouldn't run.
// Taking reference on MVC will increase size of deployment for non-MVC apps.

...which doesn't make much sense as the OpenTelemetry.Instrumentation.AspNetCore library already references the MVC framework and is useless outside of an MVC context.

Is your feature request related to a problem?

Endpoint routing and other non-attribute route patterns are not caught by the activity naming logic.

Describe the solution you'd like:

I'd like the code to be updated to support endpoint routing, which provides a wider array of options in development.

Describe alternatives you've considered.

Right now I have to manually set the enricher to handle it, like:

services.AddOpenTelemetryTracing((builder) =>
  builder
    .AddAspNetCoreInstrumentation(b => b.Enrich = (activity, eventName, data) =>
    {
      HttpContext context;
      if (data is HttpRequest request)
      {
        context = request.HttpContext;
      }
      else if (data is HttpResponse response)
      {
        context = response.HttpContext;
      }
      else
      {
        return;
      }

      if (context.Features.Get<IEndpointFeature>()?.Endpoint is RouteEndpoint endpoint)
      {
        // We like display name like: GET /normalized-lower-case-route
        // so it's easier to differentiate and sort, but that's just our pref. You can
        // see how we get the route pattern, though.
        activity.DisplayName = $"{context.Request.Method.ToUpperInvariant()} {endpoint.RoutePattern?.RawText?.ToLowerInvariant()}";
      }
      else
      {
        // Fall back to path.
        activity.DisplayName = $"{context.Request.Method.ToUpperInvariant()} {context.Request.Path.ToString().ToLowerInvariant()}";
      }
    })
    .AddHttpClientInstrumentation()
    .AddConsoleExporter());

Additional Context

It would be nice if the enrichment mechanism was easier to wire up. Having to cast the data and check types like this is sort of painful given the ASP.NET Core instrumentation generally already knows what kind of data it's passing for each event type. Having something more strongly typed would be nice.

services.AddOpenTelemetryTracing((builder) =>
  builder
    .AddAspNetCoreInstrumentation(b =>
      {
        b.Enrich.OnStartActivity = (activity, httpRequest) =>
        {
          // Use the already-cast HttpRequest object to enrich the activity.
        }
      });

tillig avatar Mar 08 '22 00:03 tillig

Hi, is there a plan for this? Resolving span name (activity.DisplayName) from request path may produce a huge amount of span names which may break the tracing infrastructure (indexing, aggregating, usage limiting, etc.) I think we should avoid producing unbounded span name values as possible as we can, in OpenTracing, it uses HTTP {method} as the span name (see https://github.com/opentracing-contrib/csharp-netcore/blob/master/src/OpenTracing.Contrib.NetCore/AspNetCore/HostingOptions.cs#L58)

Also, IMO, it may be better if span names can respect route name from RouteNameMetadata(configured by .WithName({name})) and endpoint.DisplayName (configured by .WithDisplayName("{name}"), which has a default value as '{protocal}: {method} {pattern}')

Any ideas?

suraciii avatar May 23 '22 08:05 suraciii

The issue is still there.

We use minimal APIs, and this leads to bloating with thousands of operations like "/api/orders/xxxxxxx" instead of single "/api/orders/{id}".

image

stilettk avatar Oct 21 '22 08:10 stilettk

@vishweshbankwar Lets take a look at this.

cijothomas avatar Oct 21 '22 16:10 cijothomas

BTW I have tried to use a workaround using Enrich from the original post, but it didn't work for me because Endpoint is always null.

I know that middlewares which want to use .GetEndpoint() muse be registered between .UseRouting() and .UseEndpoints(), but here I don't have any control over it.

stilettk avatar Nov 02 '22 13:11 stilettk

Given with how many cases in modern ASP.NET Core this leads to high cardinality traces it would be great to have this as part of the upcoming release. I just stumbled over this when trying the RC and it unfortunately makes the instrumentation unusable for my use-case right now. Even in some existing projects with MVC I encountered things like customized error handling triggering this which resulted in high cardinality traces for failing requests. I would expect many new users trying the release for the first time once it comes out to encounter the same problem resulting in bug reports.

The PR by @vishweshbankwar looks like it would fix this but it is still marked as a draft and "stale" so I am not sure if that is blocked on something.

hacst avatar Mar 01 '23 09:03 hacst

fixed in https://github.com/open-telemetry/opentelemetry-dotnet/pull/5026

vishweshbankwar avatar Nov 17 '23 20:11 vishweshbankwar