Skip to content

Clarify routes may contain static and dynamic segments#2734

Merged
joaopgrassi merged 12 commits intoopen-telemetry:mainfrom
matt-hensley:http-route-definition
Sep 16, 2025
Merged

Clarify routes may contain static and dynamic segments#2734
joaopgrassi merged 12 commits intoopen-telemetry:mainfrom
matt-hensley:http-route-definition

Conversation

@matt-hensley
Copy link
Copy Markdown
Contributor

@matt-hensley matt-hensley commented Sep 4, 2025

Fixes #2616

Changes

Alternative proposal to #2717 with additional language to explain intent behind "static" and "dynamic" route segments and how their value should be handled.

Merge requirement checklist

  • CONTRIBUTING.md guidelines followed.
  • Change log entry added, according to the guidelines in When to add a changelog entry.
    • If your PR does not need a change log, start the PR title with [chore]
  • Links to the prototypes or existing instrumentations (when adding or changing conventions)

Copy link
Copy Markdown
Member

@lmolkova lmolkova left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

I like the clarifications and examples of how routes look like in different frameworks, but I still don't get why we'd change stable instrumentation behavior and change routing info provided by the framework.

Comment thread docs/registry/attributes/http.md Outdated
Comment thread docs/registry/attributes/http.md Outdated
Comment thread docs/registry/attributes/http.md
Comment thread docs/http/http-spans.md Outdated
@RassK
Copy link
Copy Markdown

RassK commented Sep 4, 2025

I like the clarifications and examples of how routes look like in different frameworks, but I still don't get why we'd change stable instrumentation behavior and change routing info provided by the framework.

Because customers don't like how there is no cardinality (only 1 grouping) in asp.net which is {controller}/{action}/{id?}. There is absolutely 0 value, especially if there are no other route templates used.

@lmolkova
Copy link
Copy Markdown
Member

lmolkova commented Sep 4, 2025

I like the clarifications and examples of how routes look like in different frameworks, but I still don't get why we'd change stable instrumentation behavior and change routing info provided by the framework.

Because customers don't like how there is no cardinality (only 1 grouping) in asp.net which is {controller}/{action}/{id?}. There is absolutely 0 value, especially if there are no other route templates used.

I'm with you - what I don't get is where do we produce absolutely useless {controller}/{action}/{id?} - do you have a repro?

@RassK
Copy link
Copy Markdown

RassK commented Sep 4, 2025

I like the clarifications and examples of how routes look like in different frameworks, but I still don't get why we'd change stable instrumentation behavior and change routing info provided by the framework.

Because customers don't like how there is no cardinality (only 1 grouping) in asp.net which is {controller}/{action}/{id?}. There is absolutely 0 value, especially if there are no other route templates used.

I'm with you - what I don't get is where do we produce absolutely useless {controller}/{action}/{id?} - do you have a repro?

I'm not sure I follow 100%.
https://opentelemetry.io/docs/languages/dotnet/traces/getting-started-aspnetcore/
The default asp.net core template + opentelemetry with asp.net core tracing.

(See more faulting spans at #2616 (comment))

produces:
Activity.DisplayName: GET {controller=Home}/{action=Index}/{id?}
http.route: {controller=Home}/{action=Index}/{id?}

Activity.TraceId:            0afccf0559cddc29dfaf5c808d44d2f9
Activity.SpanId:             ff3deeb995003d11
Activity.TraceFlags:         Recorded
Activity.DisplayName:        GET {controller=Home}/{action=Index}/{id?}
Activity.Kind:               Server
Activity.StartTime:          2025-09-04T20:19:03.3173784Z
Activity.Duration:           00:00:00.1346969
Activity.Tags:
    server.address: localhost
    server.port: 5218
    http.request.method: GET
    url.scheme: http
    url.path: /
    network.protocol.version: 1.1
    user_agent.original: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0
    http.route: {controller=Home}/{action=Index}/{id?}
    http.response.status_code: 200
Instrumentation scope (ActivitySource):
    Name: Microsoft.AspNetCore
Resource associated with Activity:
    service.name: WebApplication2
    service.instance.id: 00052429-df01-4204-aaff-de18e8e6f3a7
    telemetry.sdk.name: opentelemetry
    telemetry.sdk.language: dotnet
    telemetry.sdk.version: 1.12.0

@lmolkova
Copy link
Copy Markdown
Member

lmolkova commented Sep 4, 2025

what's the problem with http.route: {controller=Home}/{action=Index}/{id?}? it shows which controller and action were hit (home and index). What would you capture if not that?

@RassK
Copy link
Copy Markdown

RassK commented Sep 4, 2025

what's the problem with http.route: {controller=Home}/{action=Index}/{id?}? it shows which controller and action were hit (home and index). What would you capture if not that?

No, in terms of ASP.NET it's the syntax to say which controller and action is default if you hit the root. (see url.path: /)

See this example (there is no {id} even, because ? allows it to miss):
url.path: /Home/Privacy
http.route: {controller=Home}/{action=Index}/{id?}
Activity.DisplayName: GET {controller=Home}/{action=Index}/{id?}

Activity.TraceId:            aa64df047db6b9bb0cf5a1c01765b0a5
Activity.SpanId:             a732ab6df880c40b
Activity.TraceFlags:         Recorded
Activity.DisplayName:        GET {controller=Home}/{action=Index}/{id?}
Activity.Kind:               Server
Activity.StartTime:          2025-09-04T20:29:37.2783252Z
Activity.Duration:           00:00:00.0096519
Activity.Tags:
    server.address: localhost
    server.port: 5218
    http.request.method: GET
    url.scheme: http
    url.path: /Home/Privacy
    network.protocol.version: 1.1
    user_agent.original: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0
    http.route: {controller=Home}/{action=Index}/{id?}
    http.response.status_code: 200
Instrumentation scope (ActivitySource):
    Name: Microsoft.AspNetCore
Resource associated with Activity:
    service.name: WebApplication2
    service.instance.id: 2fe20641-f8df-4f9c-9604-6eaa3555c702
    telemetry.sdk.name: opentelemetry
    telemetry.sdk.language: dotnet
    telemetry.sdk.version: 1.12.0

@RassK
Copy link
Copy Markdown

RassK commented Sep 4, 2025

@lmolkova
Copy link
Copy Markdown
Member

lmolkova commented Sep 4, 2025

I think I was able to repro. So the problem statement as I understand this:

  1. ASP.NET Core instrumentation does not use the best API to get actual routing data. E.g. when I use MVC app with default routing, current instrumentation populates default template when it shouldn't. See more on this below
  2. Semconv is not precise enough to tell where info should come from. Suggested something in https://github.com/open-telemetry/semantic-conventions/pull/2734/files#r2323288932 to hopefully help with it.

More on p1: let's say I create

...
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}")
    .WithStaticAssets();
...

public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;
    public HomeController(ILogger<HomeController> logger)  { _logger = logger; }

    public IActionResult Index() { return View();  }

    public IActionResult Privacy()
    {
        _logger.LogInformation("Privacy page requested, RoutePattern {}, routedata: controller {}, action {}",
            (HttpContext.GetEndpoint() as RouteEndpoint)?.RoutePattern.RawText,
            RouteData.Values.GetValueOrDefault("controller"),
            RouteData.Values.GetValueOrDefault("action"));
        return View();
    }
}

What I see in the log is

info: MvcMovie.Controllers.HomeController[0]
      Privacy page requested, RoutePattern {controller=Home}/{action=Index}/{id?}, 
      routedata controller Home, action Privacy

OTel instrumentation uses RoutePattern.RawText which is {controller=Home}/{action=Index}/{id?} which is indeed wrong and useless, while RouteData is more precise.

To me it sounds like we need to figure out what the right behavior for ASP.NET Core instrumentation is - do we know it already?

@RassK
Copy link
Copy Markdown

RassK commented Sep 4, 2025

To me it sounds like we need to figure out what's the right behavior for ASP.NET Core instrumentation is - do we know it already?

My answer would be general standardization to offer unified experience. /users/get/{id} or /users/get/:id would only benefit the developer, as they have the knowledge of the framework. Monitoring teams (who mostly purchases such products imo) can monitor multiple frameworks, so they do not see it as a unified experience.

Why this little string even matters that much, is because of the overall metrics of (statistically) interesting class of Spans. In context of web frameworks, those metrics are similar like database query performance metrics (so you always want to group similar queries).

The other option is to detach span name from http.route and leave http.route as is provided by the framework.

@lmolkova
Copy link
Copy Markdown
Member

lmolkova commented Sep 4, 2025

@RassK could you folks please create an issue for ASP.NET Core instrumentation, suggest the fix that would make span names AND routes useful for ASP.NET Core and once you have consensus, we'd make sure semconv can accomodate it?

update: it's within ASP.NET Core instrumentation freedom on what to put into http.route. What it puts today does not seem to work well with MVC - let's fix it.

@github-actions github-actions Bot added enhancement New feature or request area:http labels Sep 5, 2025
@lmolkova
Copy link
Copy Markdown
Member

lmolkova commented Sep 5, 2025

@matt-hensley I think you need to re-generate markdown from yaml to make checks pass, could you please also undraft the PR? it looks great otherwise

@matt-hensley
Copy link
Copy Markdown
Contributor Author

@matt-hensley I think you need to re-generate markdown from yaml to make checks pass, could you please also undraft the PR? it looks great otherwise

Saw those checks fail, resolving some issues with my Windows env and the docs tooling

@matt-hensley matt-hensley marked this pull request as ready for review September 8, 2025 16:54
@matt-hensley matt-hensley requested review from a team as code owners September 8, 2025 16:54
@matt-hensley matt-hensley requested review from a team as code owners September 8, 2025 16:54
@lmolkova lmolkova moved this to Needs More Approval in Semantic Conventions Triage Sep 8, 2025
Comment thread model/http/registry.yaml Outdated
Comment thread model/http/registry.yaml Outdated
@lmolkova lmolkova moved this from Needs More Approval to Ready to be Merged in Semantic Conventions Triage Sep 14, 2025
joaopgrassi and others added 3 commits September 16, 2025 10:28
Co-authored-by: Trask Stalnaker <[email protected]>
Co-authored-by: Trask Stalnaker <[email protected]>
Signed-off-by: Joao Grassi <[email protected]>
@joaopgrassi joaopgrassi added this pull request to the merge queue Sep 16, 2025
Merged via the queue into open-telemetry:main with commit e2dab49 Sep 16, 2025
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:http enhancement New feature or request

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Improve guidelines to HTTP span name generation

5 participants