Skip to content

feat(hono): add orchestrion-based auto-instrumentation - #24497

Draft
mydea wants to merge 4 commits into
feat/hono-move-to-server-utilsfrom
feat/hono-orchestrion-instrumentation
Draft

mydea wants to merge 4 commits into
feat/hono-move-to-server-utilsfrom
feat/hono-orchestrion-instrumentation

Conversation

@mydea

@mydea mydea commented Sep 18, 2026

Copy link
Copy Markdown
Member

Second of two stacked PRs splitting the Hono instrumentation rework (originally #24371). Stacked on #24496 — review/merge that first; the diff here is against the base PR's branch.

Adds honoIntegration, the auto-instrumentation that hooks Hono through the orchestrion module transform (node:diagnostics_channel) so requests are route-enriched without a manual sentry() middleware, plus its manual counterpart honoMiddleware. Registers it in getTracingIntegrations() and re-exports both from the server runtimes (node, cloudflare, bun, deno, and the serverless / meta-framework packages).

Also adds the orchestrion transform config for hono, node-integration-tests for the auto-instrumentation, and a new orchestrion-based hono-4 e2e app (the middleware-based app now lives as hono-4-legacy, added in the base PR). node-mastra now asserts route-enriched Hono spans in prod, where Hono is external and orchestrion-instrumented.

🤖 Generated with Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

@github-actions

github-actions Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 29.09 kB - -
@sentry/browser - with treeshaking flags 27.35 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 27.26 kB - -
@sentry/browser (incl. Tracing) 50.56 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 50.58 kB - -
@sentry/browser (incl. Tracing, Profiling) 53.56 kB - -
@sentry/browser (incl. Tracing, Replay) 90.11 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 79.21 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 94.81 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 107.78 kB - -
@sentry/browser (incl. Feedback) 46.62 kB - -
@sentry/browser (incl. sendFeedback) 34.15 kB - -
@sentry/browser (incl. FeedbackAsync) 39.26 kB - -
@sentry/browser (incl. Metrics) 30.1 kB - -
@sentry/browser (incl. Logs) 30.35 kB - -
@sentry/browser (incl. Metrics & Logs) 31.02 kB - -
@sentry/react 30.84 kB - -
@sentry/react (incl. Tracing) 52.88 kB - -
@sentry/vue 36.34 kB - -
@sentry/vue (incl. Tracing) 52.86 kB - -
@sentry/svelte 29.11 kB - -
CDN Bundle 30.8 kB - -
CDN Bundle (incl. Tracing) 51.1 kB - -
CDN Bundle (incl. Logs, Metrics) 33.06 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 53.09 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 73.75 kB - -
CDN Bundle (incl. Tracing, Replay) 88.66 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 90.58 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 94.7 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 96.71 kB - -
CDN Bundle - uncompressed 91.16 kB - -
CDN Bundle (incl. Tracing) - uncompressed 152.53 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 97.73 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 158.49 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 227.14 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 272.1 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 278.04 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 285.81 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 291.74 kB - -
@sentry/nextjs (client) 55.21 kB - -
@sentry/sveltekit (client) 50.99 kB - -
@sentry/core/server 40 kB - -
@sentry/core/browser 13.66 kB - -
⛔️ @sentry/node (max: 135 kB) 135.29 kB +1.49% +1.98 kB 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 82.44 kB +0.34% +273 B 🔺
@sentry/node - without tracing 91.78 kB +2.08% +1.87 kB 🔺
@sentry/node - without channel injection 113.87 kB +1.55% +1.74 kB 🔺
@sentry/aws-serverless 100.03 kB +1.89% +1.85 kB 🔺
@sentry/cloudflare (withSentry) - minified 204.74 kB - -
@sentry/cloudflare (withSentry) 509.41 kB - -

View base workflow run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread packages/server-utils/src/integrations/hono/createHonoMiddleware.ts
Comment thread packages/server-utils/src/integrations/hono/honoIntegration.ts
if (!IS_DEV) {
expect(attrValue(serverSpan, 'sentry.segment.name.source')).toBe('route');
expect(attrValue(serverSpan, 'http.route')).toBe('/manual-route');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Conditional assertions in one test

Low Severity

This was flagged because the testing conventions ask to split conditionals in a single test into separate tests per path. Both Mastra e2e tests branch on IS_DEV inside one case, so the route-enriched prod assertions and the un-enriched dev assertions never run as independent tests.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit 74f5d86. Configure here.

@mydea
mydea force-pushed the feat/hono-orchestrion-instrumentation branch from d3071cb to bef613e Compare September 21, 2026 08:13

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread dev-packages/e2e-tests/test-applications/hono-4/package.json
// A Hono `matchResult[0]` entry: `[[handler, routeMeta], paramIndexMap]`. `compose` reads the handler
// at `entry[0][0]`; the `matchedRoutes` getter reads `routeMeta` at `entry[0][1]`.
// oxlint-disable-next-line typescript/no-explicit-any
type MatchedHandlerEntry = [[any, any], any];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unguarded any in Hono integration

Low Severity

New SDK source uses any (and oxlint suppressions) on MatchedHandlerEntry, channel arguments, and tracingChannel payloads without a comment explaining why a safer type is not possible. This was flagged because the review rules require that explanation on each new any in production code.

Additional Locations (2)
Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit bef613e. Configure here.

mydea and others added 4 commits September 21, 2026 11:04
Add `honoIntegration`, the auto-instrumentation that hooks Hono through the
orchestrion module transform (`node:diagnostics_channel`) so requests are
route-enriched without a manual `sentry()` middleware, plus its manual
counterpart `honoMiddleware`. Register it in `getTracingIntegrations()` and
re-export both from the server runtimes (node, cloudflare, bun, deno, and the
serverless/meta-framework packages).

Adds the orchestrion transform config for `hono`, node-integration-tests for
the auto-instrumentation, and a new orchestrion-based `hono-4` e2e app (the
middleware-based app now lives as `hono-4-legacy`). node-mastra now asserts
route-enriched Hono spans in prod, where Hono is external and instrumented.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
An internal app.request() runs in a new Hono context but the same isolation scope,
so the request-handling dedup short-circuited the inner Sentry middleware before it
could capture context.error. When an outer handler swallowed a failed internal
response (degrading to a 200), the inner route's error was never reported. The
deduplicated middleware now still captures its own context's error, without
re-naming the transaction or overwriting request data.

Also streamline the orchestrion hono config comments, add a named-function
middleware span test, and add node-integration coverage for the inner-error case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The auto-instrumentation injection loop used a raw arity check to decide which
matched handlers to wrap as middleware spans. Use the shared isMiddleware helper
instead, which unwraps onError-composed sub-app handlers before checking arity — a
case the inline check missed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Arity alone cannot tell a middleware from a route handler declared with an unused
`next` param. The matched entries carry their registration routeMeta, so apply the
same positional heuristic as wrapSubAppMiddleware: within a method+path group the
last handler is the route handler and earlier ones are middleware; `.use()` (method
'ALL') falls back to arity. Adds node-integration coverage for the arity-2 route
handler case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mydea
mydea force-pushed the feat/hono-orchestrion-instrumentation branch from bef613e to 8941894 Compare September 21, 2026 09:10

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8941894. Configure here.

expect(attrValue(serverSpan!, 'sentry.segment.name.source')).toBe('route');
expect(attrValue(serverSpan!, 'http.route')).toMatch(/^\/api\/agents\/:[^/]+\/generate$/);
expect(serverSpan!.name).toMatch(/^POST \/api\/agents\/:[^/]+\/generate$/);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Conditional assertions in Mastra tests

Low Severity

These tests branch on IS_DEV inside a single case instead of splitting prod and dev. The dev path also never asserts that http.route and sentry.segment.name.source stay absent, so the un-instrumented Hono behavior is not actually locked in. This is flagged because the review rules require splitting conditional tests and asserting omitted payload fields.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit 8941894. Configure here.

Comment on lines +108 to +121
/**
* Per-request Context hook: the heart of the automatic instrumentation.
*
* `#dispatch` builds `new Context(req, { matchResult })` before its single-handler fast-path check,
* passing the live `matchResult` array. We:
* 1. wrap the already-matched MIDDLEWARE handlers (arity ≥ 2) for spans — route handlers (arity < 2)
* are covered by the request span and left as-is;
* 2. prepend the Sentry request/response middleware, so it runs first in the composed chain. That
* both drives route naming / request data / error capture (from inside the chain, with the
* Context) and forces the ≥2-handler `compose` path, so there is no fast-path gap.
*
* All of this runs per request, so it works on Cloudflare (no module-scope publish) and needs no
* app-instance patching or app-construction hook.
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This comment is not up-to-date with the "detection" code anymore.


const effectiveShouldHandleError =
(scope[HONO_SHOULD_HANDLE_ERROR] as SentryHonoMiddlewareOptions['shouldHandleError']) ?? shouldHandleError;
responseHandler(context, effectiveShouldHandleError);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we maybe clear the flags (HONO_REQUEST_HANDLED and HONO_SHOULD_HANDLE_ERROR) here after the response?

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.

2 participants