-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(hono): Instrument middlewares app.use()
#19611
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
abeef91
e4a35b8
4f0e13f
4b014ce
e540965
f7057ae
a09b775
58bb050
0f39821
0e032d6
429d418
8e7268c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| import { | ||
| captureException, | ||
| SEMANTIC_ATTRIBUTE_SENTRY_OP, | ||
| SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, | ||
| SPAN_STATUS_ERROR, | ||
| SPAN_STATUS_OK, | ||
| startInactiveSpan, | ||
| } from '@sentry/core'; | ||
| import type { Hono, MiddlewareHandler } from 'hono'; | ||
|
|
||
| const MIDDLEWARE_ORIGIN = 'auto.middleware.hono'; | ||
|
|
||
| // Module-level counter for anonymous middleware span names | ||
| let MIDDLEWARE_IDX = 0; | ||
|
cursor[bot] marked this conversation as resolved.
Outdated
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. l/q: In Cloudflare one isolate could handle more requests. Currently I am not 100% sure every if every request has their own bundled code (ergo starting at 0). Have you tried it out on Cloudflare how it is behaving under load when deployed?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually removed the numbering again. OTel also just uses
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool. I like that better |
||
|
|
||
| /** | ||
| * Patches `app.use` so that every middleware registered through it is automatically | ||
| * wrapped in a Sentry span. Supports both forms: `app.use(...handlers)` and `app.use(path, ...handlers)`. | ||
| */ | ||
| export function patchAppUse(app: Hono): void { | ||
| app.use = new Proxy(app.use, { | ||
| apply(target: typeof app.use, thisArg: typeof app, args: Parameters<typeof app.use>): ReturnType<typeof app.use> { | ||
| const [first, ...rest] = args as [unknown, ...MiddlewareHandler[]]; | ||
|
|
||
| if (typeof first === 'string') { | ||
| const wrappedHandlers = rest.map(handler => wrapMiddlewareWithSpan(handler, MIDDLEWARE_IDX++)); | ||
| return Reflect.apply(target, thisArg, [first, ...wrappedHandlers]); | ||
| } | ||
|
|
||
| const allHandlers = [first as MiddlewareHandler, ...rest].map(handler => | ||
| wrapMiddlewareWithSpan(handler, MIDDLEWARE_IDX++), | ||
| ); | ||
| return Reflect.apply(target, thisArg, allHandlers); | ||
| }, | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Wraps a Hono middleware handler so that its execution is traced as a Sentry span. | ||
| * Uses startInactiveSpan so that all middleware spans are siblings under the request/transaction | ||
| * (onion order: A → B → handler → B → A does not nest B under A in the trace). | ||
| */ | ||
| function wrapMiddlewareWithSpan(handler: MiddlewareHandler, index: number): MiddlewareHandler { | ||
| const spanName = handler.name || `<anonymous.${index}>`; | ||
|
|
||
| return async function sentryTracedMiddleware(context, next) { | ||
| const span = startInactiveSpan({ | ||
| name: spanName, | ||
| op: 'middleware.hono', | ||
| onlyIfParent: true, | ||
| attributes: { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'middleware.hono', | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: MIDDLEWARE_ORIGIN, | ||
| }, | ||
| }); | ||
|
|
||
| try { | ||
| const result = await handler(context, next); | ||
| span.setStatus({ code: SPAN_STATUS_OK }); | ||
| return result; | ||
| } catch (error) { | ||
| span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); | ||
| captureException(error, { | ||
| mechanism: { handled: false, type: MIDDLEWARE_ORIGIN }, | ||
| }); | ||
| throw error; | ||
|
s1gr1d marked this conversation as resolved.
|
||
| } finally { | ||
| span.end(); | ||
| } | ||
| }; | ||
| } | ||

Uh oh!
There was an error while loading. Please reload this page.