Skip to content

Expand http.route tag when DD_TRACE_EXPAND_ROUTE_TEMPLATES_ENABLED is true#8479

Draft
chojomok wants to merge 1 commit intomasterfrom
mohammad/fix-expand-http-route-templates
Draft

Expand http.route tag when DD_TRACE_EXPAND_ROUTE_TEMPLATES_ENABLED is true#8479
chojomok wants to merge 1 commit intomasterfrom
mohammad/fix-expand-http-route-templates

Conversation

@chojomok
Copy link
Copy Markdown
Collaborator

Summary

NOTE: This is coming from Tahir's PR which is from a fork: #8320
Making this not from a fork to help reduce turn around time on this

  • When DD_TRACE_EXPAND_ROUTE_TEMPLATES_ENABLED=true, the http.route span tag now reflects the expanded route template, consistent with resource_name
  • Previously, resource_name was expanded (e.g., GET /api/users/{id}) but http.route retained the raw template (e.g., api/{controller}/{id}), causing the Datadog endpoints page to show duplicate entries
  • Fix covers ASP.NET MVC, Web API 2 (.NET Framework), and ASP.NET Core (both endpoint and non-endpoint routing)

Changes

  • AspNetMvcIntegration.cs: Extract expanded route from computed resource name and use it for HttpRoute tag
  • AspNetWebApi2Integration.cs: Same pattern as MVC integration
  • AspNetCoreDiagnosticObserver.cs: Use resourcePathName (already expanded by SimplifyRoutePattern) for AspNetCoreRoute/trackingFeature.Route instead of raw template text
  • AspNetResourceNameHelperTests.cs: Added tests verifying expanded route extraction is consistent with resource name, and that different controllers produce distinct http.route values

Test plan

  • Unit tests added for route expansion consistency
  • Regression test: multiple controllers sharing a {controller} template produce distinct http.route values when expand is enabled
  • Verify existing CalculateResourceName tests still pass (no behavioral change when expandRouteTemplates=false)
  • Integration test with ASP.NET MVC app using generic {controller} routes

Fixes #8319

🤖 Generated with Claude Code

@chojomok chojomok requested review from a team as code owners April 17, 2026 16:58
Copy link
Copy Markdown
Member

@andrewlock andrewlock left a comment

Choose a reason for hiding this comment

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

Just requesting changes here because I think this needs a bigger discussion, and it seems like a breaking change 😄

@chojomok chojomok marked this pull request as draft April 17, 2026 17:03
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f9d8d6166b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +182 to +183
[InlineData("{tenantId}/{controller}/{id}", "/home/index/{id}", false, "{tenantId}/{controller}/{id}")]
[InlineData("{tenantId}/{controller}/{id}", "/home/index/{id}", true, "/{tenantid}/home/{id}")]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Fix tenantId route expectations in theory data

These two InlineData rows expect "/home/index/{id}" for the {tenantId}/{controller}/{id} template, but AspNetResourceNameHelper.CalculateResourceName cannot produce that shape: it only substitutes parameters present in the template, so there is no /index segment, and tenantId="abc123" remains {tenantid} (identifier-like) even when expansion is enabled. As written, this theory will fail on NETFRAMEWORK test runs and won’t validate the intended regression path.

Useful? React with 👍 / 👎.

@dd-trace-dotnet-ci-bot
Copy link
Copy Markdown

Execution-Time Benchmarks Report ⏱️

Execution-time results for samples comparing This PR (8479) and master.

✅ No regressions detected - check the details below

Full Metrics Comparison

FakeDbCommand

Metric Master (Mean ± 95% CI) Current (Mean ± 95% CI) Change Status
.NET Framework 4.8 - Baseline
duration71.97 ± (71.98 - 72.29) ms73.18 ± (73.15 - 73.48) ms+1.7%✅⬆️
.NET Framework 4.8 - Bailout
duration77.12 ± (77.06 - 77.48) ms79.43 ± (79.16 - 79.75) ms+3.0%✅⬆️
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1071.36 ± (1072.18 - 1078.98) ms1074.48 ± (1073.72 - 1081.33) ms+0.3%✅⬆️
.NET Core 3.1 - Baseline
process.internal_duration_ms22.37 ± (22.33 - 22.42) ms22.61 ± (22.55 - 22.67) ms+1.1%✅⬆️
process.time_to_main_ms83.19 ± (83.01 - 83.37) ms85.75 ± (85.39 - 86.12) ms+3.1%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.91 ± (10.91 - 10.92) MB10.91 ± (10.90 - 10.91) MB-0.1%
runtime.dotnet.threads.count12 ± (12 - 12)12 ± (12 - 12)+0.0%
.NET Core 3.1 - Bailout
process.internal_duration_ms22.76 ± (22.70 - 22.82) ms22.54 ± (22.49 - 22.59) ms-1.0%
process.time_to_main_ms88.21 ± (87.90 - 88.53) ms87.53 ± (87.22 - 87.83) ms-0.8%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.88 ± (10.88 - 10.89) MB10.93 ± (10.93 - 10.94) MB+0.5%✅⬆️
runtime.dotnet.threads.count13 ± (13 - 13)13 ± (13 - 13)+0.0%
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms228.18 ± (227.06 - 229.29) ms228.93 ± (227.65 - 230.22) ms+0.3%✅⬆️
process.time_to_main_ms526.94 ± (525.57 - 528.31) ms527.56 ± (526.13 - 528.99) ms+0.1%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed48.40 ± (48.37 - 48.43) MB48.45 ± (48.42 - 48.47) MB+0.1%✅⬆️
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)+0.0%
.NET 6 - Baseline
process.internal_duration_ms21.42 ± (21.37 - 21.46) ms21.38 ± (21.31 - 21.45) ms-0.2%
process.time_to_main_ms74.05 ± (73.78 - 74.32) ms73.97 ± (73.65 - 74.29) ms-0.1%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.60 ± (10.60 - 10.60) MB10.61 ± (10.61 - 10.61) MB+0.1%✅⬆️
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 6 - Bailout
process.internal_duration_ms21.10 ± (21.07 - 21.14) ms21.46 ± (21.41 - 21.52) ms+1.7%✅⬆️
process.time_to_main_ms73.60 ± (73.42 - 73.77) ms76.25 ± (75.98 - 76.52) ms+3.6%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.71 ± (10.71 - 10.72) MB10.73 ± (10.73 - 10.73) MB+0.2%✅⬆️
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms385.42 ± (383.20 - 387.65) ms384.91 ± (383.19 - 386.64) ms-0.1%
process.time_to_main_ms527.72 ± (526.53 - 528.91) ms526.44 ± (525.13 - 527.76) ms-0.2%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed49.90 ± (49.87 - 49.93) MB50.06 ± (50.04 - 50.09) MB+0.3%✅⬆️
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)-0.1%
.NET 8 - Baseline
process.internal_duration_ms19.60 ± (19.54 - 19.66) ms19.45 ± (19.42 - 19.49) ms-0.8%
process.time_to_main_ms73.35 ± (73.11 - 73.59) ms72.49 ± (72.32 - 72.66) ms-1.2%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.66 ± (7.66 - 7.67) MB7.65 ± (7.64 - 7.65) MB-0.2%
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 8 - Bailout
process.internal_duration_ms19.81 ± (19.75 - 19.87) ms19.67 ± (19.61 - 19.72) ms-0.7%
process.time_to_main_ms76.22 ± (75.94 - 76.50) ms75.07 ± (74.81 - 75.32) ms-1.5%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.72 ± (7.71 - 7.72) MB7.70 ± (7.70 - 7.71) MB-0.1%
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms301.93 ± (299.57 - 304.30) ms304.54 ± (302.17 - 306.92) ms+0.9%✅⬆️
process.time_to_main_ms489.32 ± (488.16 - 490.49) ms491.54 ± (490.31 - 492.76) ms+0.5%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed37.06 ± (37.03 - 37.08) MB37.05 ± (37.03 - 37.07) MB-0.0%
runtime.dotnet.threads.count27 ± (27 - 27)27 ± (27 - 27)+0.0%✅⬆️

HttpMessageHandler

Metric Master (Mean ± 95% CI) Current (Mean ± 95% CI) Change Status
.NET Framework 4.8 - Baseline
duration208.95 ± (208.84 - 209.80) ms208.57 ± (208.37 - 209.18) ms-0.2%
.NET Framework 4.8 - Bailout
duration214.50 ± (214.26 - 215.08) ms213.56 ± (213.38 - 214.18) ms-0.4%
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1217.84 ± (1218.50 - 1225.26) ms1222.52 ± (1223.91 - 1230.68) ms+0.4%✅⬆️
.NET Core 3.1 - Baseline
process.internal_duration_ms204.96 ± (204.49 - 205.43) ms203.11 ± (202.70 - 203.52) ms-0.9%
process.time_to_main_ms90.02 ± (89.76 - 90.28) ms89.27 ± (88.91 - 89.63) ms-0.8%
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed15.96 ± (15.93 - 15.98) MB15.91 ± (15.88 - 15.94) MB-0.3%
runtime.dotnet.threads.count20 ± (20 - 20)20 ± (20 - 20)-0.3%
.NET Core 3.1 - Bailout
process.internal_duration_ms203.43 ± (202.94 - 203.92) ms202.89 ± (202.46 - 203.31) ms-0.3%
process.time_to_main_ms90.07 ± (89.80 - 90.34) ms90.89 ± (90.61 - 91.17) ms+0.9%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed16.02 ± (15.99 - 16.04) MB15.94 ± (15.92 - 15.96) MB-0.5%
runtime.dotnet.threads.count21 ± (21 - 21)21 ± (20 - 21)-1.2%
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms414.35 ± (413.09 - 415.61) ms415.22 ± (413.91 - 416.53) ms+0.2%✅⬆️
process.time_to_main_ms545.44 ± (544.21 - 546.68) ms550.23 ± (548.82 - 551.64) ms+0.9%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed59.34 ± (59.29 - 59.38) MB59.39 ± (59.34 - 59.43) MB+0.1%✅⬆️
runtime.dotnet.threads.count30 ± (30 - 30)30 ± (30 - 30)+0.0%✅⬆️
.NET 6 - Baseline
process.internal_duration_ms209.71 ± (209.19 - 210.23) ms208.50 ± (208.02 - 208.98) ms-0.6%
process.time_to_main_ms77.98 ± (77.67 - 78.28) ms77.41 ± (77.17 - 77.65) ms-0.7%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.20 ± (16.17 - 16.22) MB16.18 ± (16.16 - 16.20) MB-0.1%
runtime.dotnet.threads.count20 ± (19 - 20)20 ± (19 - 20)-0.1%
.NET 6 - Bailout
process.internal_duration_ms208.09 ± (207.71 - 208.47) ms208.90 ± (208.53 - 209.26) ms+0.4%✅⬆️
process.time_to_main_ms78.73 ± (78.55 - 78.92) ms79.18 ± (79.00 - 79.37) ms+0.6%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.27 ± (16.24 - 16.29) MB16.18 ± (16.16 - 16.21) MB-0.5%
runtime.dotnet.threads.count20 ± (20 - 21)21 ± (20 - 21)+0.3%✅⬆️
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms601.46 ± (598.67 - 604.26) ms600.32 ± (597.25 - 603.39) ms-0.2%
process.time_to_main_ms544.27 ± (543.07 - 545.46) ms546.84 ± (545.46 - 548.23) ms+0.5%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed61.68 ± (61.56 - 61.80) MB61.74 ± (61.63 - 61.84) MB+0.1%✅⬆️
runtime.dotnet.threads.count31 ± (31 - 31)31 ± (31 - 31)+0.0%✅⬆️
.NET 8 - Baseline
process.internal_duration_ms206.92 ± (206.53 - 207.31) ms208.92 ± (208.43 - 209.42) ms+1.0%✅⬆️
process.time_to_main_ms76.81 ± (76.57 - 77.05) ms77.80 ± (77.53 - 78.06) ms+1.3%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.57 ± (11.54 - 11.59) MB11.56 ± (11.54 - 11.59) MB-0.0%
runtime.dotnet.threads.count19 ± (19 - 19)19 ± (19 - 19)+0.1%✅⬆️
.NET 8 - Bailout
process.internal_duration_ms206.09 ± (205.68 - 206.51) ms206.94 ± (206.60 - 207.28) ms+0.4%✅⬆️
process.time_to_main_ms78.05 ± (77.84 - 78.26) ms78.17 ± (77.99 - 78.34) ms+0.2%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.61 ± (11.58 - 11.63) MB11.66 ± (11.64 - 11.69) MB+0.5%✅⬆️
runtime.dotnet.threads.count20 ± (20 - 20)20 ± (20 - 20)+0.3%✅⬆️
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms523.79 ± (519.02 - 528.56) ms529.62 ± (524.54 - 534.71) ms+1.1%✅⬆️
process.time_to_main_ms507.21 ± (506.16 - 508.27) ms510.46 ± (509.51 - 511.40) ms+0.6%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed50.66 ± (50.61 - 50.71) MB50.72 ± (50.66 - 50.77) MB+0.1%✅⬆️
runtime.dotnet.threads.count30 ± (30 - 30)30 ± (30 - 30)+0.2%✅⬆️
Comparison explanation

Execution-time benchmarks measure the whole time it takes to execute a program, and are intended to measure the one-off costs. Cases where the execution time results for the PR are worse than latest master results are highlighted in **red**. The following thresholds were used for comparing the execution times:

  • Welch test with statistical test for significance of 5%
  • Only results indicating a difference greater than 5% and 5 ms are considered.

Note that these results are based on a single point-in-time result for each branch. For full results, see the dashboard.

Graphs show the p99 interval based on the mean and StdDev of the test run, as well as the mean value of the run (shown as a diamond below the graph).

Duration charts
FakeDbCommand (.NET Framework 4.8)
gantt
    title Execution time (ms) FakeDbCommand (.NET Framework 4.8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8479) - mean (73ms)  : 71, 76
    master - mean (72ms)  : 70, 74

    section Bailout
    This PR (8479) - mean (79ms)  : 75, 84
    master - mean (77ms)  : 74, 80

    section CallTarget+Inlining+NGEN
    This PR (8479) - mean (1,078ms)  : 1021, 1134
    master - mean (1,076ms)  : 1025, 1126

Loading
FakeDbCommand (.NET Core 3.1)
gantt
    title Execution time (ms) FakeDbCommand (.NET Core 3.1)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8479) - mean (115ms)  : 109, 121
    master - mean (112ms)  : 108, 116

    section Bailout
    This PR (8479) - mean (117ms)  : 112, 122
    master - mean (118ms)  : 112, 125

    section CallTarget+Inlining+NGEN
    This PR (8479) - mean (794ms)  : 772, 816
    master - mean (793ms)  : 770, 816

Loading
FakeDbCommand (.NET 6)
gantt
    title Execution time (ms) FakeDbCommand (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8479) - mean (102ms)  : 96, 107
    master - mean (102ms)  : 96, 108

    section Bailout
    This PR (8479) - mean (104ms)  : 99, 109
    master - mean (101ms)  : 99, 103

    section CallTarget+Inlining+NGEN
    This PR (8479) - mean (942ms)  : 908, 976
    master - mean (942ms)  : 911, 972

Loading
FakeDbCommand (.NET 8)
gantt
    title Execution time (ms) FakeDbCommand (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8479) - mean (100ms)  : 96, 103
    master - mean (101ms)  : 96, 106

    section Bailout
    This PR (8479) - mean (103ms)  : 97, 109
    master - mean (104ms)  : 99, 109

    section CallTarget+Inlining+NGEN
    This PR (8479) - mean (830ms)  : 793, 866
    master - mean (823ms)  : 784, 861

Loading
HttpMessageHandler (.NET Framework 4.8)
gantt
    title Execution time (ms) HttpMessageHandler (.NET Framework 4.8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8479) - mean (209ms)  : 205, 213
    master - mean (209ms)  : 204, 214

    section Bailout
    This PR (8479) - mean (214ms)  : 210, 218
    master - mean (215ms)  : 211, 218

    section CallTarget+Inlining+NGEN
    This PR (8479) - mean (1,227ms)  : 1179, 1276
    master - mean (1,222ms)  : 1173, 1271

Loading
HttpMessageHandler (.NET Core 3.1)
gantt
    title Execution time (ms) HttpMessageHandler (.NET Core 3.1)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8479) - mean (302ms)  : 295, 309
    master - mean (304ms)  : 298, 311

    section Bailout
    This PR (8479) - mean (303ms)  : 297, 310
    master - mean (303ms)  : 296, 310

    section CallTarget+Inlining+NGEN
    This PR (8479) - mean (1,005ms)  : 975, 1036
    master - mean (996ms)  : 961, 1031

Loading
HttpMessageHandler (.NET 6)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8479) - mean (295ms)  : 288, 301
    master - mean (297ms)  : 289, 304

    section Bailout
    This PR (8479) - mean (297ms)  : 292, 302
    master - mean (296ms)  : 290, 302

    section CallTarget+Inlining+NGEN
    This PR (8479) - mean (1,180ms)  : 1142, 1217
    master - mean (1,175ms)  : 1146, 1203

Loading
HttpMessageHandler (.NET 8)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8479) - mean (297ms)  : 289, 306
    master - mean (294ms)  : 288, 301

    section Bailout
    This PR (8479) - mean (296ms)  : 291, 301
    master - mean (295ms)  : 288, 301

    section CallTarget+Inlining+NGEN
    This PR (8479) - mean (1,074ms)  : 1002, 1146
    master - mean (1,063ms)  : 1003, 1122

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DD_TRACE_EXPAND_ROUTE_TEMPLATES_ENABLED does not expand http.route tag, only resource_name

2 participants