Skip to content

Conversation

Karibash
Copy link
Contributor

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
Contributor 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
Contributor 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
Contributor 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
Contributor 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
Contributor 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
Contributor 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

@Karibash
Copy link
Contributor Author

Hi @s1gr1d,
This PR was approved a week ago — do you have any plans to merge it?
I’ve created a new PR related to this one, so if there are no issues, I’d appreciate it if you could merge it.

@s1gr1d s1gr1d merged commit 3bf4a30 into getsentry:develop Aug 25, 2025
109 checks passed
@s1gr1d
Copy link
Member

s1gr1d commented Aug 25, 2025

Sorry, we were busy with Hackweek last week (an internal hackathon) but I merged it now :)

@Karibash Karibash deleted the feature/hono-instrumentation branch August 25, 2025 11:21
Lms24 pushed a commit that referenced this pull request Aug 25, 2025
This PR adds the external contributor to the CHANGELOG.md file, so that
they are credited for their contribution. See #17366

Co-authored-by: s1gr1d <32902192+s1gr1d@users.noreply.github.com>
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