feat(observability): per-request performance timing → Server-Timing (perf-tuning mode)#2411
Merged
Merged
Conversation
…perf-tuning mode)
Surface per-request server-side timing to clients via the W3C `Server-Timing`
response header so a slow request's phase breakdown shows up directly in the
browser DevTools Network → Timing panel.
`@objectstack/observability`:
- New `PerfTiming` collector — a tiny, dependency-free per-request accumulator
of named phase durations, plus an `AsyncLocalStorage`-backed ambient API
(`runWithPerfTiming` / `currentPerfTiming`, and the no-op-when-disabled free
functions `measureServerTiming` / `startServerTiming` / `recordServerTiming`)
so any code on the request's async chain can record a phase without threading
a request object through every layer.
- `formatServerTiming` serializes marks to the spec grammar, coercing names to
tokens and stripping quotes/backslashes/control chars from descriptions to
prevent header injection.
`@objectstack/plugin-hono-server`:
- Opt-in middleware (registered before CORS) brackets the whole request as
`total` and establishes the ambient collector; the HTTP adapter contributes
`parse` (body parse) and `handler` (route handler) sub-phases.
- Off by default — the header is a backend-fingerprinting surface. Enable with
`new HonoServerPlugin({ serverTiming: true })` or `OS_SERVER_TIMING=true`
(works through the default `os serve`). Disabled = zero-overhead no-ops.
Docs: new "Server-Timing (perf-tuning mode)" section in docs/OBSERVABILITY.md.
Tests: 17 unit (collector/serializer/ambient) + 4 adapter integration
(header present when enabled with total+handler, absent by default, env toggle,
explicit-false override).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 5 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…/polynomial-redos) The `/^_+|_+$/g` regex backtracks polynomially on underscore-heavy input; since formatServerTiming is a public export the name is uncontrolled. Replace the trim with a linear charCode scan. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds opt-in per-request performance timing surfaced via the W3C
Server-Timingresponse header — so a slow request's phase breakdown renders directly in the browser DevTools Network → Timing panel, no profiler attach required.Why opt-in ("perf-tuning mode")
The header discloses internal phase durations, which is great for profiling but also lets a caller fingerprint the backend. So it's off by default and treated as a toggle you flip in staging (or briefly in prod behind an allowlist), consistent with how
OS_CORS_*/OS_MCP_*gate other adapter behaviour.How
@objectstack/observability(newperf-timing.ts)PerfTiming— tiny, dependency-free per-request collector (record/start/measure).AsyncLocalStorage:runWithPerfTiming/currentPerfTiming, plus the no-op-when-disabled free functionsmeasureServerTiming/startServerTiming/recordServerTiming, so any code on the request's async chain can add a phase without threading a request object through every layer.formatServerTimingserializes to the spec grammar, coercing names to tokens and stripping"/\/ control chars from descriptions (no header injection).@objectstack/plugin-hono-servertotaland establishes the ambient collector.parse(body parse) andhandler(route handler) sub-phases into whatever collector is active — no-ops when the feature is off.new HonoServerPlugin({ serverTiming: true })orOS_SERVER_TIMING=true(works through the defaultos serve, which constructs the plugin for you). Explicit option wins over the env var.Enabling
Tests
perf-timing.test.ts): serializer formatting/rounding/token-sanitization/injection-stripping,PerfTimingrecord/start-idempotency/measure-on-throw, ambient propagation acrossawait.server-timing.test.ts, real Hono adapter): header absent by default, present withtotal+handlerwhenserverTiming: true, enabled viaOS_SERVER_TIMING=true, and explicitserverTiming: falseoverrides the env var.turbo build(incl. tsup DTS) and rooteslintclean.Docs
New "Server-Timing (perf-tuning mode)" section + go-live checklist item in
docs/OBSERVABILITY.md.Risk
Default behaviour is unchanged — the header only appears when explicitly enabled, and all timing call sites are zero-overhead no-ops when disabled. No existing response-header assertions are affected.
🤖 Generated with Claude Code