caddy/modules/caddyhttp
Varun Chawla dc36082859
caddyhttp: Collect metrics once per route instead of per handler (#7492)
* perf: collect metrics once per route instead of per handler (#4644)

Move Prometheus metrics instrumentation from the per-handler level to
the per-route level. Previously, every middleware handler in a route was
individually wrapped with metricsInstrumentedHandler, causing metrics to
be collected N times per request (once per handler in the chain). Since
all handlers in a route see the same request, these per-handler metrics
were redundant and added significant CPU overhead (73% of request
handling time per the original profiling).

The fix introduces metricsInstrumentedRoute which wraps the entire
compiled handler chain once in wrapRoute, collecting metrics only when
the route actually matches. The handler label uses the first handler's
module name, which is the most meaningful identifier for the route.

Benchmark results (5 handlers per route):
  Old (per-handler):  ~4650 ns/op, 4400 B/op, 45 allocs/op
  New (per-route):    ~940 ns/op,  816 B/op,   8 allocs/op
  Improvement:        ~5x faster, ~5.4x less memory, ~5.6x fewer allocs

Signed-off-by: Varun Chawla <varun_6april@hotmail.com>

* Remove unused metricsInstrumentedHandler code

Delete the metricsInstrumentedHandler type, its constructor, and
ServeHTTP method since they are no longer used after switching to
route-level metrics collection via metricsInstrumentedRoute. Also
remove the unused metrics parameter from wrapMiddleware and the
middlewareHandlerFunc test helper, and convert existing tests to
use the new route-level API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Address review feedback: restore comments, move function to bottom

- Move computeApproximateRequestSize back to bottom of file to minimize diff
- Restore all useful comments that were accidentally dropped
- Old metricsInstrumentedHandler already removed in previous commit

---------

Signed-off-by: Varun Chawla <varun_6april@hotmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 15:15:55 -07:00
..
caddyauth chore: Add nolints to work around haywire linters (#7493) 2026-02-17 16:52:54 -07:00
encode Revert "encode: Implement Flush for legacy compatibility" 2026-02-27 14:14:19 -07:00
fileserver fileserver: Fix tests on Windows 2026-02-20 11:46:45 -07:00
headers chore: Enable modernize linter (#7519) 2026-02-26 14:01:35 -07:00
intercept intercept: use already buffered response if possible when intercepting (#7028) 2025-10-21 04:48:07 +00:00
logging logging: log_append Early option, Supports {http.response.body} (#7368) 2025-12-16 23:42:42 -05:00
map chore: Use slices package where possible (#6585) 2024-09-25 14:30:56 -06:00
proxyprotocol docs: expand proxy protocol docs (#6620) 2024-10-10 16:21:26 -04:00
push chore: Dumb prealloc lint fix (#7430) 2026-01-13 14:13:43 -05:00
requestbody chore: ugh, lint fix... (#7275) 2025-09-26 03:14:48 -04:00
reverseproxy api: Add all in-flight requests /reverse_proxy/upstreams (Fixes #7277) (#7517) 2026-03-03 15:14:55 -07:00
rewrite chore: Dumb prealloc lint fix (#7430) 2026-01-13 14:13:43 -05:00
standard
templates docs: add maybe template function documentation (#7388) 2025-12-06 06:51:28 -05:00
tracing tracing: Add span attributes to tracing module (#7269) 2025-12-31 11:33:18 -07:00
app.go caddyhttp: {http.request.body_base64} placeholder (#7367) 2025-12-13 21:01:12 -07:00
autohttps.go autohttps: Ensure CertMagic config is recreated after autohttps runs (#7510) 2026-03-03 14:44:06 -07:00
caddyhttp.go caddyhttp: Add MatchWithError to replace SetVar hack (#6596) 2024-11-04 23:18:50 +00:00
caddyhttp_test.go
celmatcher.go chore: Add nolints to work around haywire linters (#7493) 2026-02-17 16:52:54 -07:00
celmatcher_test.go use a more modern writing style to simplify code (#7182) 2025-08-20 11:41:21 -06:00
errors.go use math/rand/v2 instead of math/rand (#7413) 2026-02-11 09:15:51 -07:00
http2listener.go caddyhttp: wrap accepted connection to suppress tls.ConnectionState (#7247) 2025-10-16 03:13:40 +00:00
httpredirectlistener.go
invoke.go
ip_matchers.go caddyhttp: refactor to use reflect.TypeFor (#7187) 2025-08-18 17:08:46 -06:00
ip_range.go Move PrivateRangesCIDR() back: add a pass-through function (#6514) 2024-08-12 05:47:05 -04:00
logging.go core: custom slog handlers for modules (log contextual data) (#7346) 2025-11-12 13:29:47 -07:00
marshalers.go caddyhttp: create a placeholder for and log ech status (#7328) 2025-12-07 16:01:58 +00:00
matchers.go Merge commit from fork 2026-02-20 10:54:50 -07:00
matchers_test.go Merge commit from fork 2026-02-20 10:54:50 -07:00
metrics.go caddyhttp: Collect metrics once per route instead of per handler (#7492) 2026-03-03 15:15:55 -07:00
metrics_test.go caddyhttp: Collect metrics once per route instead of per handler (#7492) 2026-03-03 15:15:55 -07:00
replacer.go caddyhttp: Evaluate tls.client placeholders more accurately (fix #7530) (#7534) 2026-02-28 22:03:18 -07:00
replacer_test.go caddyhttp: add replacer placeholders for escaped values (#7181) 2025-08-25 09:07:51 -06:00
responsematchers.go caddyhttp: Allow matching Transfer-Encoding, add to access logs (#6629) 2024-12-20 11:16:34 -07:00
responsematchers_test.go
responsewriter.go caddyhttp: ResponseRecorder sets stream regardless of 1xx 2025-01-27 08:18:37 -07:00
responsewriter_test.go
routes.go caddyhttp: Collect metrics once per route instead of per handler (#7492) 2026-03-03 15:15:55 -07:00
server.go caddyhttp: Limit empty Host check to HTTP/1.1 2026-02-27 10:22:39 -07:00
server_test.go caddyhttp: Add trusted_proxies_unix for trusting unix socket X-Forwarded-* headers (#7265) 2025-10-16 02:47:32 +00:00
staticerror.go caddyhttp: run error (msg) through replacer (#6536) 2024-08-22 11:32:44 -06:00
staticresp.go chore: Add nolints to work around haywire linters (#7493) 2026-02-17 16:52:54 -07:00
staticresp_test.go
subroute.go
vars.go caddyhttp: refactor to use reflect.TypeFor (#7187) 2025-08-18 17:08:46 -06:00