metrics.ts50 lines · main
| 1 | import type { MiddlewareHandler } from 'hono'; |
| 2 | |
| 3 | import { incCounter, observeHistogram } from '../lib/metrics.js'; |
| 4 | import { recordAuthRoute5xx } from '../services/auth-reliability.js'; |
| 5 | |
| 6 | /** |
| 7 | * Records `http_requests_total{method,status,route}` and |
| 8 | * `http_request_duration_ms{method,route}` for every request that reaches |
| 9 | * a handler. Uses Hono's matched-route pattern (`c.req.routePath`) rather |
| 10 | * than the raw path so dynamic segments don't blow up label cardinality — |
| 11 | * `/v1/projects/p_abc.../functions/foo` becomes the same label as |
| 12 | * `/v1/projects/p_xyz.../functions/bar`. |
| 13 | * |
| 14 | * Skips its own `/metrics` endpoint to keep prometheus scrapes from |
| 15 | * polluting the metric they're scraping. |
| 16 | */ |
| 17 | export const metricsMiddleware = (): MiddlewareHandler => async (c, next) => { |
| 18 | // Hono v4 exposes the matched pattern via `routePath`. Falls back to the |
| 19 | // raw path if for some reason it's missing (older middleware order, etc). |
| 20 | const start = performance.now(); |
| 21 | await next(); |
| 22 | const durationMs = performance.now() - start; |
| 23 | |
| 24 | const route = |
| 25 | typeof (c.req as { routePath?: unknown }).routePath === 'string' |
| 26 | ? ((c.req as { routePath: string }).routePath || c.req.path) |
| 27 | : c.req.path; |
| 28 | |
| 29 | if (route === '/metrics') return; |
| 30 | |
| 31 | const labels = { |
| 32 | method: c.req.method, |
| 33 | route, |
| 34 | }; |
| 35 | incCounter('http_requests_total', { ...labels, status: String(c.res.status) }); |
| 36 | observeHistogram('http_request_duration_ms', durationMs, labels); |
| 37 | |
| 38 | // S6.3: auth-path 5xx for operator snapshot + Prometheus. |
| 39 | const status = c.res.status; |
| 40 | if (status >= 500) { |
| 41 | const path = c.req.path; |
| 42 | if ( |
| 43 | path.includes('/auth') || |
| 44 | path.includes('/auth-tenant') || |
| 45 | path.includes('/v1/auth') |
| 46 | ) { |
| 47 | recordAuthRoute5xx(); |
| 48 | } |
| 49 | } |
| 50 | }; |