Repository navigation
Pre-size the HttpLogging parameter lists - #70181
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
The capacity reservations overallocate request storage and allocate unused response arrays in some logging configurations, causing unnecessary allocations on every request.
2 open issues
What changed in this PR
This PR pre-sizes HTTP logging parameter lists to reduce intermediate array allocations while preserving log contents and ordering.
Changes:
- Reserves capacity before collecting request and response properties and headers.
- Adds parameterized coverage for header and interceptor-parameter ordering with separate and combined logs.
| File | Description |
|---|---|
src/Middleware/HttpLogging/test/HttpLoggingMiddlewareTests.cs |
Tests exact log ordering with many headers and interceptor-added parameters. |
src/Middleware/HttpLogging/src/HttpLoggingMiddleware.cs |
Adds a shared capacity helper and request/response reservations. |
Review effort: Balanced
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/azp run aspnetcore-components-e2e Only 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
No pipelines are associated with this pull request. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Both are currently failing on unrelated PRs too: #69752 (RDG-only), #70068, #70173's previous run and #70177. Over the e2e runs I've watched today they hit about 1 in 3 runs. This PR only changes 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
The (Mono client and WebAssembly) e2e leg failed again on the same two tests: 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
On 95a5749, 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
…69494) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
CI on 9d8da48 failed only on 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
CI on de89bec failed only on tests unrelated to this HttpLogging change:
The previous failure ( 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
…ocess) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
On 6a6fc86, all three Blazor E2E legs, the platform tests and the quarantined pipeline passed. The only failure was 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
…ble) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
On 14e8401, aspnetcore-ci passed (including Helix, platform tests and the Windows templates leg), and so did aspnetcore-quarantined-pr. The only failures were two Blazor E2E tests, on the Mono server and Mono client/WebAssembly legs:
Both have failed intermittently on earlier runs of this PR and passed on others (all three E2E legs were green on 6a6fc86 and 9d8da48). They also fail on unrelated PRs such as #69752 and #70068. Nothing under 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
/review |
Adds a Microsoft.AspNetCore.HttpLogging.Microbenchmarks project that runs the UseHttpLogging pipeline with default fields and with CombineLogs, using an enabled logger that formats every message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DeagleGross
left a comment
There was a problem hiding this comment.
update bencmark after change as always
|
ASP.NET Core Perf 2 stopped because the EnsureCapacity review feedback is addressed in bf9a8a9. 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Replace the per-field counting with Count + a small constant + Headers.Count (headers only when enabled), as suggested in review. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
✅ No documentation PR was created. Confidence that documentation is needed: 3% No docs needed: internal performance refactoring of HttpLoggingMiddleware (tests, benchmark, internal source only); no public API or behavior change. Documentation surfaces considered
|

Pre-size the HttpLogging parameter lists
Pre-size the
HttpLoggingMiddlewareparameter lists to avoid array growth.Description
For each logged request,
HttpLoggingMiddlewareadds the request properties and every request header toHttpLoggingInterceptorContext.InternalParameters, and later adds the status code and every response header. That list can't be pooled because theHttpLogstate may be kept by a logger. Every request starts with an empty list, so it grows 0 → 4 → 8 → 16 (→ 32) and throws away each intermediateKeyValuePair<string, object?>[].Before adding parameters, the list reserves room with
List.EnsureCapacity(Count + constant + Headers.Count), counting headers only when they are logged:InvokeInternal):Count + 6(Protocol, Method, Scheme, PathBase, Path, QueryString), plusRequest.Headers.Countwhen RequestHeaders is enabled.LogResponseHeadersCore, the shared path used by the middleware,ResponseBufferingStreamandUpgradeFeatureLoggingDecorator):Count + 1(StatusCode), plusResponse.Headers.Countwhen ResponseHeaders is enabled. Nothing is reserved when no response properties or headers are enabled.The reservation can overshoot by a few slots when only some properties are enabled, which is cheaper than keeping a per-field count in sync with the
AddParametercalls.Countalready includes any parameters interceptors added. Only the list's capacity changes. Its contents, order, log output and theParametersview seen by interceptors are identical. No public API changes.Tests
dotnet test src/Middleware/HttpLogging/test: 146/146 pass (re-run after review feedback).ManyHeadersAndInterceptorParameters_AllLoggedInOrder(separate and combined logs): 20 request and 20 response headers plus interceptor-added parameters. It asserts the exact log lines in order, which crosses several growth boundaries.Benchmark (3 interleaved rounds)
The benchmark is committed in
src/Middleware/HttpLogging/perf/Microbenchmarks/HttpLoggingMiddlewareBenchmark.cs. It builds the realAddHttpLogging+UseHttpLoggingpipeline with an enabled logger that calls the formatter, like a console sink, without doing any I/O. Each op is a newDefaultHttpContextwith 10 request headers, and the endpoint sets 3 response headers.Result (after switching to
EnsureCapacityin bf9a8a9): allocations drop in every round: −256 B per request with default fields (4,272 → 4,016 B) and −288 B withCombineLogs(4,632 → 4,344 B). The earlier exact sizing saved slightly more (−272 / −480 B). WithCombineLogs, the response stage now grows the list once more instead of using slots reserved by the request stage. The mean was lower in all 6 PR rounds (−4% to −13%), but several confidence intervals overlap andContextOnlyitself moves ±6% between rounds on this 2-core machine. I'm treating this as an allocation-only improvement, not a confirmed time win.BenchmarkDotNet v0.13.0, InProcess (
--config perflab), 5 warmup + 15 measured iterations, Server GC. Rounds ran in the order main, PR, main, PR, main, PR. Both builds use the same benchmark source; "main" is this PR's merge base (4f556f7) and "PR" is bf9a8a9. Allocated is exact bytes per op from the JSON exporter. Machine: AMD EPYC 9V74, 2 cores, Ubuntu 24.04 (container), .NET SDK 12.0.100-alpha.1.26521.103, runtime 12.0.0 (12.0.26.52911).🤖 Posted by ASP.NET Core Perf 2 (AI Agent)