Skip to content

spec: give IHttpServer a response-observing hook so HTTP metrics are transport-agnostic instead of Hono-only #9835

Description

@huangyiirene

Filed as directed by the maintainer's 2026-08-18 ruling on #9650, which chose the transport-owned seam (Option A) and named this its principled successor, to be its own packages/spec card and explicitly not folded into that fix.

Verbatim from the ruling:

Option B (IHttpServer response-observing contract hook) is the principled successor: file it as its own spec card, ⛔ not folded into this fix.

Documented expectation: a transport that does not implement the seam reports no HTTP metrics. State this plainly in the docs/PR body rather than letting it read as coverage.

Filed unassigned; not graded by the filing seat.

The gap this closes

PR #9746 emits http_requests_total{method,route,status} from the Hono adapter, as a raw-app middleware installed at the end of HonoServerPlugin.init(). That reaches every inbound request on that transport, including the getRawApp() mounts no IHttpServer-level wrapper can see — measured, at least 14 surfaces in two classes on #9650.

What it cannot reach is another transport. packages/qa/http-conformance/src/adapter.ts:37-41 registers http.server and deliberately does not implement getRawApp(); it has no equivalent seam, so it emits nothing. Zero there means "not instrumented", never "no traffic" — and that is precisely the failure mode #9650 is about, one level up. The ruling accepts it as a documented expectation for now; this card is how it stops being one.

Why the existing use() seam is not the answer — measured, not argued

packages/runtime/src/http-metrics-inbound-coverage.hono.integration.test.ts pins it: the framework-agnostic use() middleware chain runs to completion and only then returns Hono's next(), so a middleware there sees method, path, query and headers and has no response. It cannot carry the status label, which is the label the whole operator guidance (5xx rate) is keyed on. Body is deliberately absent there too, for the reasons the use() contract states.

So the contract needs something the current one cannot express: an observation point that runs after the response exists.

Sketch, for the spec seat to accept or replace

An afterResponse style registration on IHttpServer — something an adapter invokes once per request with { method, routePattern, status, elapsedMs } — plus:

Not urgent, but not free to defer indefinitely

The label shape ships with #9650. Once dashboards are wired against it, any adapter that later joins with a different label convention is a breaking change to the operator's queries rather than to any code, which is the class of change nothing in CI can catch.


Generated by Claude Code

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions