Skip to content

Commit 75251f3

Browse files
committed
feat(node): Add an instrumentation interface for Hono
1 parent f9181d2 commit 75251f3

File tree

4 files changed

+196
-0
lines changed

4 files changed

+196
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export enum AttributeNames {
2+
HONO_TYPE = 'hono.type',
3+
HONO_NAME = 'hono.name',
4+
}
5+
6+
export enum HonoTypes {
7+
MIDDLEWARE = 'middleware',
8+
REQUEST_HANDLER = 'request_handler',
9+
}
10+
11+
export enum HonoNames {
12+
MIDDLEWARE = 'middleware',
13+
REQUEST_HANDLER = 'request handler',
14+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import type { IntegrationFn } from '@sentry/core';
2+
import { defineIntegration } from '@sentry/core';
3+
import { generateInstrumentOnce } from '@sentry/node-core';
4+
import { HonoInstrumentation } from './instrumentation';
5+
6+
const INTEGRATION_NAME = 'Hono';
7+
8+
export const instrumentHono = generateInstrumentOnce(
9+
INTEGRATION_NAME,
10+
() => new HonoInstrumentation(),
11+
);
12+
13+
const _honoIntegration = (() => {
14+
return {
15+
name: INTEGRATION_NAME,
16+
setupOnce() {
17+
instrumentHono();
18+
},
19+
};
20+
}) satisfies IntegrationFn;
21+
22+
/**
23+
* Adds Sentry tracing instrumentation for [Hono](https://hono.dev/).
24+
*
25+
* If you also want to capture errors, you need to call `setupHonoErrorHandler(app)` after you set up your Hono server.
26+
*
27+
* For more information, see the [hono documentation](https://docs.sentry.io/platforms/javascript/guides/hono/).
28+
*
29+
* @example
30+
* ```javascript
31+
* const Sentry = require('@sentry/node');
32+
*
33+
* Sentry.init({
34+
* integrations: [Sentry.honoIntegration()],
35+
* })
36+
* ```
37+
*/
38+
export const honoIntegration = defineIntegration(_honoIntegration);
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { InstrumentationBase,InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';
2+
import type { HandlerInterface, Hono, HonoInstance, MiddlewareHandlerInterface, OnHandlerInterface } from './types';
3+
4+
const PACKAGE_NAME = '@sentry/instrumentation-hono';
5+
const PACKAGE_VERSION = '0.0.1';
6+
7+
/**
8+
* Hono instrumentation for OpenTelemetry
9+
*/
10+
export class HonoInstrumentation extends InstrumentationBase {
11+
public constructor() {
12+
super(PACKAGE_NAME, PACKAGE_VERSION, {});
13+
}
14+
15+
/**
16+
* Initialize the instrumentation.
17+
*/
18+
public init(): InstrumentationNodeModuleDefinition[] {
19+
return [
20+
new InstrumentationNodeModuleDefinition(
21+
'hono',
22+
['^4.0.0'],
23+
moduleExports => this._patch(moduleExports),
24+
),
25+
];
26+
}
27+
28+
/**
29+
* Patches the module exports to instrument Hono.
30+
*/
31+
private _patch(moduleExports: { Hono: Hono }): { Hono: Hono } {
32+
// eslint-disable-next-line @typescript-eslint/no-this-alias
33+
const instrumentation = this;
34+
35+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
36+
function Hono(this: HonoInstance, ...args: any): HonoInstance {
37+
const app: HonoInstance = moduleExports.Hono.apply(this, args);
38+
39+
instrumentation._wrap(app, 'get', instrumentation._patchHandler());
40+
instrumentation._wrap(app, 'post', instrumentation._patchHandler());
41+
instrumentation._wrap(app, 'put', instrumentation._patchHandler());
42+
instrumentation._wrap(app, 'delete', instrumentation._patchHandler());
43+
instrumentation._wrap(app, 'options', instrumentation._patchHandler());
44+
instrumentation._wrap(app, 'patch', instrumentation._patchHandler());
45+
instrumentation._wrap(app, 'all', instrumentation._patchHandler());
46+
instrumentation._wrap(app, 'on', instrumentation._patchOnHandler());
47+
instrumentation._wrap(app, 'use', instrumentation._patchMiddlewareHandler());
48+
49+
return app;
50+
}
51+
52+
moduleExports.Hono = Hono;
53+
return moduleExports;
54+
}
55+
56+
/**
57+
* Patches the route handler to instrument it.
58+
*/
59+
private _patchHandler(): (original: HandlerInterface) => HandlerInterface {
60+
return function(original: HandlerInterface) {
61+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
62+
return function wrappedHandler(this: HonoInstance, ...args: any) {
63+
// TODO: Add OpenTelemetry tracing logic here
64+
return original.apply(this, args);
65+
};
66+
};
67+
}
68+
69+
/**
70+
* Patches the 'on' handler to instrument it.
71+
*/
72+
private _patchOnHandler(): (original: OnHandlerInterface) => OnHandlerInterface {
73+
return function(original: OnHandlerInterface) {
74+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
75+
return function wrappedHandler(this: HonoInstance, ...args: any) {
76+
// TODO: Add OpenTelemetry tracing logic here
77+
return original.apply(this, args);
78+
};
79+
};
80+
}
81+
82+
/**
83+
* Patches the middleware handler to instrument it.
84+
*/
85+
private _patchMiddlewareHandler(): (original: MiddlewareHandlerInterface) => MiddlewareHandlerInterface {
86+
return function(original: MiddlewareHandlerInterface) {
87+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
88+
return function wrappedHandler(this: HonoInstance, ...args: any) {
89+
// TODO: Add OpenTelemetry tracing logic here
90+
return original.apply(this, args);
91+
};
92+
};
93+
}
94+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Vendored from: https://github.com/honojs/hono/blob/855e5b1adbf685bf4b3e6b76573aa7cb0a108d04/src/request.ts#L30
2+
export type HonoRequest = {
3+
path: string;
4+
};
5+
6+
// Vendored from: https://github.com/honojs/hono/blob/855e5b1adbf685bf4b3e6b76573aa7cb0a108d04/src/context.ts#L291
7+
export type Context = {
8+
req: HonoRequest;
9+
};
10+
11+
// Vendored from: https://github.com/honojs/hono/blob/855e5b1adbf685bf4b3e6b76573aa7cb0a108d04/src/types.ts#L36C1-L36C39
12+
export type Next = () => Promise<void>;
13+
14+
// Vendored from: https://github.com/honojs/hono/blob/855e5b1adbf685bf4b3e6b76573aa7cb0a108d04/src/types.ts#L73
15+
export type Handler = (c: Context, next: Next) => Promise<Response> | Response;
16+
17+
// Vendored from: https://github.com/honojs/hono/blob/855e5b1adbf685bf4b3e6b76573aa7cb0a108d04/src/types.ts#L80
18+
export type MiddlewareHandler = (c: Context, next: Next) => Promise<Response | void> | Response | void;
19+
20+
// Vendored from: https://github.com/honojs/hono/blob/855e5b1adbf685bf4b3e6b76573aa7cb0a108d04/src/types.ts#L109
21+
export type HandlerInterface = {
22+
(...handlers: (Handler | MiddlewareHandler)[]): HonoInstance;
23+
(path: string, ...handlers: (Handler | MiddlewareHandler)[]): HonoInstance;
24+
};
25+
26+
// Vendored from: https://github.com/honojs/hono/blob/855e5b1adbf685bf4b3e6b76573aa7cb0a108d04/src/types.ts#L1071
27+
export type OnHandlerInterface = {
28+
(method: string | string[], path: string | string[], ...handlers: (Handler | MiddlewareHandler)[]): HonoInstance;
29+
};
30+
31+
// Vendored from: https://github.com/honojs/hono/blob/855e5b1adbf685bf4b3e6b76573aa7cb0a108d04/src/types.ts#L679
32+
export type MiddlewareHandlerInterface = {
33+
(...handlers: MiddlewareHandler[]): HonoInstance;
34+
(path: string, ...handlers: MiddlewareHandler[]): HonoInstance;
35+
};
36+
37+
// Vendored from: https://github.com/honojs/hono/blob/855e5b1adbf685bf4b3e6b76573aa7cb0a108d04/src/hono-base.ts#L99
38+
export interface HonoInstance {
39+
get: HandlerInterface;
40+
post: HandlerInterface;
41+
put: HandlerInterface;
42+
delete: HandlerInterface;
43+
options:HandlerInterface;
44+
patch:HandlerInterface;
45+
all: HandlerInterface;
46+
on: OnHandlerInterface;
47+
use: MiddlewareHandlerInterface;
48+
}
49+
50+
export type Hono = () => HonoInstance;

0 commit comments

Comments
 (0)