Skip to content

feat(observability): per-request performance timing → Server-Timing (perf-tuning mode)#2411

Merged
os-zhuang merged 2 commits into
mainfrom
feat/server-timing-perf-tuning
Jun 28, 2026
Merged

feat(observability): per-request performance timing → Server-Timing (perf-tuning mode)#2411
os-zhuang merged 2 commits into
mainfrom
feat/server-timing-perf-tuning

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

What

Adds opt-in per-request performance timing surfaced via the W3C Server-Timing response header — so a slow request's phase breakdown renders directly in the browser DevTools Network → Timing panel, no profiler attach required.

Server-Timing: total;dur=18.7;desc="Total server time", parse;dur=0.4;desc="Body parse", handler;dur=17.9;desc="Route handler"

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 (new perf-timing.ts)

  • PerfTiming — tiny, dependency-free per-request collector (record / start / measure).
  • Ambient API over AsyncLocalStorage: runWithPerfTiming / currentPerfTiming, plus the no-op-when-disabled free functions measureServerTiming / startServerTiming / recordServerTiming, so any code on the request's async chain can add a phase without threading a request object through every layer.
  • formatServerTiming serializes to the spec grammar, coercing names to tokens and stripping " / \ / control chars from descriptions (no header injection).

@objectstack/plugin-hono-server

  • Outer 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 into whatever collector is active — no-ops when the feature is off.
  • Enable via new HonoServerPlugin({ serverTiming: true }) or OS_SERVER_TIMING=true (works through the default os serve, which constructs the plugin for you). Explicit option wins over the env var.

Enabling

new HonoServerPlugin({ serverTiming: true });
OS_SERVER_TIMING=true os serve

Tests

  • 17 unit (perf-timing.test.ts): serializer formatting/rounding/token-sanitization/injection-stripping, PerfTiming record/start-idempotency/measure-on-throw, ambient propagation across await.
  • 4 integration (server-timing.test.ts, real Hono adapter): header absent by default, present with total+handler when serverTiming: true, enabled via OS_SERVER_TIMING=true, and explicit serverTiming: false overrides the env var.
  • Full suites green: observability 51/51, plugin-hono-server 44/44. turbo build (incl. tsup DTS) and root eslint clean.

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

…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>
@vercel

vercel Bot commented Jun 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 28, 2026 2:31am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file tests tooling labels Jun 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/observability, @objectstack/plugin-hono-server.

5 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/implementation-status.mdx (via @objectstack/observability, @objectstack/plugin-hono-server)
  • content/docs/concepts/packages.mdx (via @objectstack/plugin-hono-server)
  • content/docs/guides/authentication.mdx (via @objectstack/plugin-hono-server)
  • content/docs/guides/packages.mdx (via @objectstack/plugin-hono-server)
  • content/docs/guides/plugins.mdx (via @objectstack/plugin-hono-server)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Comment thread packages/observability/src/perf-timing.ts Fixed
…/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>
@os-zhuang
os-zhuang merged commit dc2990f into main Jun 28, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the feat/server-timing-perf-tuning branch June 28, 2026 03:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants