* 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>
* caddyfile: add `observe_catchall_hosts` option
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
* correct JSON field name and doc comment
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
---------
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
* metrics: move `metrics` up, outside `servers`
This change moves the metrics configuration from per-server level to a single config knob within the `http` app. Enabling `metrics` in any of the configured servers inside `http` enables metrics for all servers.
Fix#6604
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
* normalize domain name
---------
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
* Add per host config
* Pass host label when option is enabled
* Test per host enabled
* metrics: scope metrics per loaded config
* doc and linter
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
* inject the custom registry into the admin handler
Co-Authored-By: Dave Henderson <dhenderson@gmail.com>
* remove `TODO` comment
* fixes
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
* refactor to delay metrics admin handler provision
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
---------
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
Co-authored-by: Hussam Almarzooq <me@hussam.io>
Co-authored-by: Dave Henderson <dhenderson@gmail.com>
* use gofmput to format code
* use gci to format imports
* reconfigure gci
* linter autofixes
* rearrange imports a little
* export GOOS=windows golangci-lint run ./... --fix
* metrics: Always track method label in uppercase
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
* Just use strings.ToUpper for clarity
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
* metrics: Fixing panic while observing with bad exemplars
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
* Minor cleanup
The server is already added to the context. So, we can simply use that
to get the server name, which is a field on the server.
* Add integration test for auto HTTP->HTTPS redirects
A test like this would have caught the problem in the first place
Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>