Skip to content

feat: wrap route handlers with tracing at build time#4240

Merged
pi0 merged 10 commits into
nitrojs:mainfrom
logaretm:fix/wrap-find-route-with-tracing
May 8, 2026
Merged

feat: wrap route handlers with tracing at build time#4240
pi0 merged 10 commits into
nitrojs:mainfrom
logaretm:fix/wrap-find-route-with-tracing

Conversation

@logaretm
Copy link
Copy Markdown
Member

@logaretm logaretm commented Apr 29, 2026

Summary

  • Wraps compiled route handlers with wrapHandlerWithTracing from h3/tracing during codegen, ensuring route traces are emitted for file-based routing with zero per-request overhead
  • Removes the runtime wrapFindRouteWithTracing call from the tracing virtual module — tracing is now applied at build time in the routing virtual module
  • Only active when tracingChannel.h3 is enabled; otherwise codegen is unchanged

Companion PR

Test plan

  • Build a nitro app with tracingChannel.h3 enabled and verify route handlers emit h3.request diagnostics_channel traces
  • Build without tracing enabled and verify no h3/tracing import in generated routing module

🤖 Generated with Claude Code

Nitro resolves file-based routes at request time via h3["~findRoute"],
bypassing h3["~routes"]. The h3 tracing plugin only wraps routes
registered through ~routes / .on() / .use(), so file-based route
handlers were never traced — only middleware spans were emitted.

Use the new wrapFindRouteWithTracing helper exported from h3/tracing
to wrap ~findRoute so route handler spans are emitted too.

Ref: h3js/h3#1369

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 29, 2026 15:49
@logaretm logaretm requested a review from pi0 as a code owner April 29, 2026 15:50
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 29, 2026

@logaretm is attempting to deploy a commit to the Nitro Team on Vercel.

A member of the Team first needs to authorize it.

@logaretm logaretm marked this pull request as draft April 29, 2026 15:50
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

Adds conditional H3 tracing to the generated routing module: compute traceH3 from config, conditionally import wrapHandlerWithTracing, pass { tracing: traceH3 } to handler serialization, and wrap serialized handlers with wrapHandlerWithTracing(...) when enabled.

Changes

H3 Tracing Support

Layer / File(s) Summary
Configuration & Conditional Import
src/build/virtual/routing.ts
traceH3 boolean is derived from nitro.options.tracingChannel?.h3; wrapHandlerWithTracing import is conditionally injected.
Routing Serializer Invocation
src/build/virtual/routing.ts
findRoute now calls serializeHandler with { tracing: traceH3 } to signal tracing-enabled serialization.
serializeHandler Extension
src/build/virtual/routing.ts
serializeHandler gains an optional { tracing?: boolean } parameter and wraps the final serialized handler expression with wrapHandlerWithTracing(...) when tracing is true, preserving multi-handler composition behavior.
Tests
test/unit/virtual-routing.test.ts
New Vitest tests add a Nitro stub helper and three cases asserting presence/absence of h3/tracing import and wrapHandlerWithTracing(...) wrapping based on tracingChannel.h3 values.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: wrap route handlers with tracing at build time' follows conventional commits format with a clear 'feat' type prefix and accurately describes the main change of wrapping route handlers with tracing during build time.
Description check ✅ Passed The pull request description is clearly related to the changeset, detailing the wrapping of route handlers with h3/tracing during codegen, removal of runtime wrapping, and conditional activation based on tracingChannel.h3 configuration.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Wrap Nitro’s file-based route resolution (h3["~findRoute"]) with the h3 tracing helper so route handlers discovered at request time emit h3.request tracing events with type: "route".

Changes:

  • Import wrapFindRouteWithTracing from h3/tracing when tracingChannel.h3 is enabled
  • Emit generated setup code that wraps nitroApp.h3["~findRoute"] after installing the h3 tracing plugin

Comment thread src/build/virtual/tracing.ts Outdated
Comment thread src/build/virtual/tracing.ts Outdated
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 29, 2026

Open in StackBlitz

npm i https://pkg.pr.new/nitro@4240

commit: a2a002a

Use h3's new `wrapHandlerWithTracing` export to wrap compiled route
handlers during codegen instead of patching ~findRoute at runtime.
This ensures route traces are emitted with zero per-request overhead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@logaretm logaretm changed the title fix(tracing): wrap ~findRoute handlers with h3 tracing channel feat: wrap route handlers with tracing at build time Apr 29, 2026
autofix-ci Bot and others added 2 commits April 29, 2026 16:16
import wrapHandlerWithTracing from h3/tracing export
@logaretm logaretm marked this pull request as ready for review May 5, 2026 18:07
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/build/virtual/routing.ts (1)

95-112: ⚡ Quick win

tracedSerializeHandler duplicates serializeHandler's body — extract a factory.

The two functions are structurally identical; they diverge only in whether the handler expression is wrapped with __wrapHandler__(...). Any future addition to the serialized shape (e.g. a new metadata field) must be kept in sync manually across both.

A closure factory eliminates the duplication while remaining compatible with compileToString's (h) => string callback contract:

♻️ Proposed refactor — factory-based serializer
-function serializeHandler(h: MaybeArray<NitroEventHandler & { _importHash: string }>): string {
-  const meta = Array.isArray(h) ? h[0] : h;
-
-  return `{${[
-    `route:${JSON.stringify(meta.route)}`,
-    meta.method && `method:${JSON.stringify(meta.method)}`,
-    meta.meta && `meta:${JSON.stringify(meta.meta)}`,
-    `handler:${
-      Array.isArray(h)
-        ? `multiHandler(${h.map((handler) => serializeHandlerFn(handler)).join(",")})`
-        : serializeHandlerFn(h)
-    }`,
-  ]
-    .filter(Boolean)
-    .join(",")}}`;
-}
-
-function tracedSerializeHandler(
-  h: MaybeArray<NitroEventHandler & { _importHash: string }>
-): string {
-  const meta = Array.isArray(h) ? h[0] : h;
-
-  return `{${[
-    `route:${JSON.stringify(meta.route)}`,
-    meta.method && `method:${JSON.stringify(meta.method)}`,
-    meta.meta && `meta:${JSON.stringify(meta.meta)}`,
-    `handler:__wrapHandler__(${
-      Array.isArray(h)
-        ? `multiHandler(${h.map((handler) => serializeHandlerFn(handler)).join(",")})`
-        : serializeHandlerFn(h)
-    })`,
-  ]
-    .filter(Boolean)
-    .join(",")}}`;
-}
+function makeSerializeHandler(wrapWith?: string) {
+  return (h: MaybeArray<NitroEventHandler & { _importHash: string }>): string => {
+    const meta = Array.isArray(h) ? h[0] : h;
+    const handlerCode = Array.isArray(h)
+      ? `multiHandler(${h.map((handler) => serializeHandlerFn(handler)).join(",")})`
+      : serializeHandlerFn(h);
+
+    return `{${[
+      `route:${JSON.stringify(meta.route)}`,
+      meta.method && `method:${JSON.stringify(meta.method)}`,
+      meta.meta && `meta:${JSON.stringify(meta.meta)}`,
+      `handler:${wrapWith ? `${wrapWith}(${handlerCode})` : handlerCode}`,
+    ]
+      .filter(Boolean)
+      .join(",")}}`;
+  };
+}
+
+const serializeHandler = makeSerializeHandler();
+const tracedSerializeHandler = makeSerializeHandler("__wrapHandler__");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/build/virtual/routing.ts` around lines 95 - 112, Extract the common
serialization logic from serializeHandler and tracedSerializeHandler into a
factory function (e.g., makeSerializeHandler) that returns a (h) => string
serializer; have makeSerializeHandler accept a single option/flag or wrapper
function to apply to the handler expression (so tracedSerializeHandler calls
makeSerializeHandler with a wrapper that wraps the handler expression with
"__wrapHandler__(...)" and serializeHandler calls it with an identity/no-op
wrapper), reuse serializeHandlerFn and multiHandler inside the factory to build
the handler expression, and update both serializeHandler and
tracedSerializeHandler to delegate to the new factory so future metadata fields
are added once in the shared factory rather than duplicated.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/build/virtual/routing.ts`:
- Around line 95-112: Extract the common serialization logic from
serializeHandler and tracedSerializeHandler into a factory function (e.g.,
makeSerializeHandler) that returns a (h) => string serializer; have
makeSerializeHandler accept a single option/flag or wrapper function to apply to
the handler expression (so tracedSerializeHandler calls makeSerializeHandler
with a wrapper that wraps the handler expression with "__wrapHandler__(...)" and
serializeHandler calls it with an identity/no-op wrapper), reuse
serializeHandlerFn and multiHandler inside the factory to build the handler
expression, and update both serializeHandler and tracedSerializeHandler to
delegate to the new factory so future metadata fields are added once in the
shared factory rather than duplicated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d43f53de-ace4-4fbf-b9b9-5414dd08b122

📥 Commits

Reviewing files that changed from the base of the PR and between c467f13 and 40a2aa9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • package.json
  • src/build/virtual/routing.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
test/unit/virtual-routing.test.ts (1)

53-54: ⚡ Quick win

Make the positive-case assertion less brittle to codegen formatting changes.

The exact toContain(...) for the full wrapper call can fail on benign serialization differences. Prefer a regex that checks semantic wrapping while allowing minor formatting/identifier variance.

Proposed test assertion tweak
-    expect(template).toContain("wrapHandlerWithTracing(h3.toEventHandler(_abc123))");
+    expect(template).toMatch(
+      /wrapHandlerWithTracing\(h3\.toEventHandler\(_[A-Za-z0-9]+\)\)/
+    );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/unit/virtual-routing.test.ts` around lines 53 - 54, The test is brittle
because it asserts the exact string for the wrapper call; change the assertion
to use a regex that checks for semantic wrapping of wrapHandlerWithTracing
around h3.toEventHandler while allowing spacing and identifier differences.
Replace the
expect(template).toContain("wrapHandlerWithTracing(h3.toEventHandler(_abc123))")
check with an assertion that template matches a regex like
/wrapHandlerWithTracing\(\s*h3\.toEventHandler\(/ (referencing the template
variable and the wrapHandlerWithTracing and h3.toEventHandler symbols) so minor
formatting or identifier changes won’t break the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/unit/virtual-routing.test.ts`:
- Around line 53-54: The test is brittle because it asserts the exact string for
the wrapper call; change the assertion to use a regex that checks for semantic
wrapping of wrapHandlerWithTracing around h3.toEventHandler while allowing
spacing and identifier differences. Replace the
expect(template).toContain("wrapHandlerWithTracing(h3.toEventHandler(_abc123))")
check with an assertion that template matches a regex like
/wrapHandlerWithTracing\(\s*h3\.toEventHandler\(/ (referencing the template
variable and the wrapHandlerWithTracing and h3.toEventHandler symbols) so minor
formatting or identifier changes won’t break the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 449e8ab8-c1d4-4f4d-9716-353620af5bcf

📥 Commits

Reviewing files that changed from the base of the PR and between 556a0ae and a2a002a.

📒 Files selected for processing (1)
  • test/unit/virtual-routing.test.ts

@pi0 pi0 merged commit d2e6490 into nitrojs:main May 8, 2026
12 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants