Repository navigation
Commit e45f3b3
authored
fix(observability): label HTTP metrics by route pattern, not raw path (#1791)
## Bug
With `observability.metrics.enabled`, `MetricsManager.HTTPMiddleware` is
the first middleware on the REST API chi router (`setupRoutes`), ahead
of `apiKeyAuthMiddleware`. It labels `mcpproxy_http_requests_total` and
`mcpproxy_http_request_duration_seconds` with `r.URL.Path`.
So every distinct path creates a new counter series and a new histogram
series (11 buckets plus sum and count), and they are never removed. That
includes unauthenticated requests that end in 401 or 404, and every
server name in `/api/v1/servers/{name}/…`.
## Trigger
With metrics enabled, any client that can reach the listener sends
requests to distinct paths, for example `GET /api/v1/<random>` in a
loop. A web page open in the user's browser can do this against
`127.0.0.1` with `fetch(..., {mode: "no-cors"})`, and no API key is
needed. The daemon's memory and the size of every `/metrics` scrape then
grow without bound.
## Fix
Label requests with the chi route pattern they matched
(`/api/v1/servers/{name}`). A request that matched no route gets one
fixed `unmatched` label. Outside a chi router, which no production
caller uses, the raw path is kept. The label name (`path`) and the other
labels are unchanged, so the Grafana dashboard, which sums by `status`,
keeps working.
## Test
`TestHTTPMiddleware_PathLabelIsRoutePattern`: a chi router with one
`/api/v1/servers/{name}` route. It receives requests for two server
names and two unknown paths. It must end up with exactly two series: the
route pattern with count 2, and `unmatched` with count 2.
## Commands run
- `CGO_ENABLED=0 go test -count=1 -run 'Test'
./internal/observability/`: ok
- On current `main`, and again with the fix reverted, the new test fails
with `expected: 2, actual: 4` series.
- `go vet ./internal/observability/`: ok1 parent 8b8a88f commit e45f3b3
2 files changed
Lines changed: 44 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| |||
431 | 432 | | |
432 | 433 | | |
433 | 434 | | |
434 | | - | |
| 435 | + | |
435 | 436 | | |
436 | 437 | | |
437 | 438 | | |
438 | 439 | | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
439 | 459 | | |
440 | 460 | | |
441 | 461 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
| 5 | + | |
4 | 6 | | |
5 | 7 | | |
6 | 8 | | |
7 | 9 | | |
| 10 | + | |
8 | 11 | | |
9 | 12 | | |
10 | 13 | | |
| |||
55 | 58 | | |
56 | 59 | | |
57 | 60 | | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
58 | 81 | | |
59 | 82 | | |
60 | 83 | | |
| |||
0 commit comments