Skip to content

feat(node): Add an instrumentation interface for Hono #17366

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

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from

Conversation

Karibash
Copy link

This PR introduces the scaffolding for Sentry’s tracing integration with Hono by adding interface-only implementations and wiring needed to verify the overall approach before filling in the tracing logic.

Summary

  • Adds a new Hono Instrumentation (OpenTelemetry-based) which patches Hono route and middleware APIs.
  • Provides a honoIntegration exported via defineIntegration, plus an instrumentHono guard using generateInstrumentOnce.
  • Introduces minimal, vendored Hono types required for patching, and enums for consistent attribute naming.

Intent & scope

  • Implemented interfaces only to validate the design direction.
    The goal is to confirm the wrapping points, attribute schema, and initialization flow before we add any span creation, context propagation, or attribute setting.
  • If this approach looks good, the next step is to ship a patch that implements the route handler tracing.
    That follow-up will include span start/finish, setting hono.type/hono.name, request path/method extraction, and trace context propagation.
  • No tests added in this PR because it only introduces the interface and structure. Tests will land together with the first functional instrumentation patch.

Rationale

There is an existing Hono OTel package (@hono/otel), but it currently lacks several features we need for a robust Sentry integration—especially middleware instrumentation and Sentry-specific integration points (e.g., seamless correlation with Sentry transactions/spans and future Sentry error handler wiring).
Given these gaps, we’re proceeding with an in-repo implementation tailored for Sentry’s needs.

Related Issue

#15260

cursor[bot]

This comment was marked as outdated.

Copy link
Author

Choose a reason for hiding this comment

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

I extracted and defined only the minimal types necessary for instrumentation from Hono source code.

Comment on lines 36 to 50
function Hono(this: HonoInstance, ...args: any): HonoInstance {
const app: HonoInstance = moduleExports.Hono.apply(this, args);

instrumentation._wrap(app, 'get', instrumentation._patchHandler());
instrumentation._wrap(app, 'post', instrumentation._patchHandler());
instrumentation._wrap(app, 'put', instrumentation._patchHandler());
instrumentation._wrap(app, 'delete', instrumentation._patchHandler());
instrumentation._wrap(app, 'options', instrumentation._patchHandler());
instrumentation._wrap(app, 'patch', instrumentation._patchHandler());
instrumentation._wrap(app, 'all', instrumentation._patchHandler());
instrumentation._wrap(app, 'on', instrumentation._patchOnHandler());
instrumentation._wrap(app, 'use', instrumentation._patchMiddlewareHandler());

return app;
}
Copy link
Author

Choose a reason for hiding this comment

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

Hono dynamically defines these methods within its constructor, which prevented overriding the prototype, so I decided to patch the constructor instead.

Comment on lines 56 to 93
/**
* Patches the route handler to instrument it.
*/
private _patchHandler(): (original: HandlerInterface) => HandlerInterface {
return function(original: HandlerInterface) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return function wrappedHandler(this: HonoInstance, ...args: any) {
// TODO: Add OpenTelemetry tracing logic here
return original.apply(this, args);
};
};
}

/**
* Patches the 'on' handler to instrument it.
*/
private _patchOnHandler(): (original: OnHandlerInterface) => OnHandlerInterface {
return function(original: OnHandlerInterface) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return function wrappedHandler(this: HonoInstance, ...args: any) {
// TODO: Add OpenTelemetry tracing logic here
return original.apply(this, args);
};
};
}

/**
* Patches the middleware handler to instrument it.
*/
private _patchMiddlewareHandler(): (original: MiddlewareHandlerInterface) => MiddlewareHandlerInterface {
return function(original: MiddlewareHandlerInterface) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return function wrappedHandler(this: HonoInstance, ...args: any) {
// TODO: Add OpenTelemetry tracing logic here
return original.apply(this, args);
};
};
}
Copy link
Author

Choose a reason for hiding this comment

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

The implementation of these patches will be submitted later across several PRs.

@Karibash Karibash force-pushed the feature/hono-instrumentation branch from 75251f3 to 0ddfe17 Compare August 10, 2025 16:22
cursor[bot]

This comment was marked as outdated.

@Karibash Karibash force-pushed the feature/hono-instrumentation branch from b40e85a to cb1a52e Compare August 11, 2025 06:45
@Karibash Karibash force-pushed the feature/hono-instrumentation branch from 6a3abac to 21fba19 Compare August 11, 2025 08:28
Copy link
Member

@s1gr1d s1gr1d left a comment

Choose a reason for hiding this comment

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

Thanks for adding this PR!

Copy link
Member

Choose a reason for hiding this comment

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

We don't use enums in our repository (also see related PR). However, I think this file can be deleted, as those enums are not used anywhere.

Copy link
Author

Choose a reason for hiding this comment

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

I have removed the enums as you pointed out.
These values will be needed when specifying span attributes, so at that time I plan to define them as constants with primitive values.
2a6cd6e

@@ -0,0 +1,88 @@
import { InstrumentationBase,InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';
Copy link
Member

Choose a reason for hiding this comment

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

Run yarn fix to fix linting and the code formatting with prettier.

Copy link
Author

Choose a reason for hiding this comment

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

I’ve fixed it. I’ll be careful not to forget next time.
98a2c37

/**
* Patches the module exports to instrument Hono.
*/
private _patch(moduleExports: { Hono: Hono }): { Hono: Hono } {
Copy link
Member

Choose a reason for hiding this comment

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

Lowercase the hono variable so it's easier to differentiate between the type and the variable.

Suggested change
private _patch(moduleExports: { Hono: Hono }): { Hono: Hono } {
private _patch(moduleExports: { hono: Hono }): { hono: Hono } {

Copy link
Author

Choose a reason for hiding this comment

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

Since we are patching the class exported by hono, wouldn’t it need to be uppercase here, same as the exported class name?
https://github.com/honojs/hono/blob/main/src/hono.ts#L16

Copy link
Member

Choose a reason for hiding this comment

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

ah this makes sense, yes :)

cursor[bot]

This comment was marked as outdated.

@s1gr1d
Copy link
Member

s1gr1d commented Aug 11, 2025

Thank you for this contribution 🙌

@Karibash Karibash force-pushed the feature/hono-instrumentation branch from 98a2c37 to e694aae Compare August 11, 2025 14:53
instrumentation._wrap(this, 'use', instrumentation._patchMiddlewareHandler());
}
};
return moduleExports;
Copy link

Choose a reason for hiding this comment

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

Bug: Hono Instrumentation Fails on Default Imports

The Hono instrumentation only patches the named Hono export, ignoring the default export. This prevents instrumentation for applications importing Hono as a default. Additionally, the Hono class is replaced in-place without using InstrumentationBase._wrap, and instance methods are wrapped within the subclass constructor. This design prevents proper unpatching, leading to persistent instrumentation where new Hono instances remain wrapped even after the instrumentation is disabled, breaking enable/disable semantics.

Fix in Cursor Fix in Web

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