Skip to content

Commit 048ce20

Browse files
authored
feat(hono): Emit warning if @sentry/node was imported instead of @sentry/hono/node (#21240)
The Hono Node setup requires a separate instrumentation file with the `Sentry.init()` call. It's important that `init` is imported from the Hono SDK and not Node. As this could easily be overlooked, but is very important for the correct setup, this warning is added.
1 parent 18ecbe9 commit 048ce20

2 files changed

Lines changed: 76 additions & 19 deletions

File tree

packages/hono/src/node/middleware.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type BaseTransportOptions, debug, type Options, getClient } from '@sentry/core';
1+
import { type BaseTransportOptions, consoleSandbox, debug, getClient, type Options } from '@sentry/core';
22
import type { Env, Hono, MiddlewareHandler } from 'hono';
33
import { requestHandler, responseHandler } from '../shared/middlewareHandlers';
44
import { applyPatches } from '../shared/applyPatches';
@@ -17,12 +17,26 @@ export interface HonoNodeOptions extends Options<BaseTransportOptions> {}
1717
export const sentry = <E extends Env>(app: Hono<E>, options?: SentryHonoMiddlewareOptions): MiddlewareHandler => {
1818
const sentryClient = getClient();
1919
if (sentryClient === undefined) {
20-
debug.warn(
21-
'Sentry is not initialized. Call `init()` from @sentry/hono/node in an `instrument.ts` file loaded via `--import` to set up Sentry for your application.',
22-
);
20+
consoleSandbox(() => {
21+
// eslint-disable-next-line no-console
22+
console.warn(
23+
'[@sentry/hono] Sentry is not initialized. Call `init()` from `@sentry/hono/node` in an `instrument.ts` file loaded via `--import` to set up Sentry for your application.',
24+
);
25+
});
2326
} else {
24-
sentryClient.getOptions().debug &&
25-
debug.log('Sentry is initialized, proceeding to set up Hono `sentry` middleware.');
27+
const isInitializedWithHonoSdk = sentryClient.getOptions()._metadata?.sdk?.name === 'sentry.javascript.hono';
28+
29+
if (!isInitializedWithHonoSdk) {
30+
consoleSandbox(() => {
31+
// eslint-disable-next-line no-console
32+
console.warn(
33+
'[Sentry] Sentry was not initialized with `@sentry/hono/node`. Please import from `@sentry/hono/node` to ensure Hono-specific instrumentation is applied correctly.',
34+
);
35+
});
36+
} else {
37+
sentryClient.getOptions().debug &&
38+
debug.log('Sentry is initialized, proceeding to set up Hono `sentry` middleware.');
39+
}
2640
}
2741

2842
applyPatches(app);

packages/hono/test/node/middleware.test.ts

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,25 +62,61 @@ describe('Hono Node Middleware', () => {
6262
expect(middleware.constructor.name).toBe('AsyncFunction');
6363
});
6464

65-
it('emits a warning when Sentry is not initialized', () => {
66-
const warnSpy = vi.spyOn(SentryCore.debug, 'warn');
65+
it('emits a console.warn when Sentry is not initialized', () => {
66+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
6767
vi.spyOn(SentryCore, 'getClient').mockReturnValue(undefined);
6868

6969
const app = new Hono();
7070
sentry(app);
7171

72-
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('Sentry is not initialized'));
72+
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining('Sentry is not initialized'));
73+
consoleWarnSpy.mockRestore();
7374
});
7475

75-
it('does not emit a warning when Sentry is already initialized', () => {
76-
const warnSpy = vi.spyOn(SentryCore.debug, 'warn');
76+
it('does not emit a warning when Sentry is already initialized with @sentry/hono/node', () => {
77+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
78+
const fakeClient = {
79+
getOptions: () => ({
80+
debug: false,
81+
_metadata: { sdk: { name: 'sentry.javascript.hono' } },
82+
}),
83+
};
84+
vi.spyOn(SentryCore, 'getClient').mockReturnValue(fakeClient as unknown as SentryCore.Client);
85+
86+
const app = new Hono();
87+
sentry(app);
88+
89+
expect(consoleWarnSpy).not.toHaveBeenCalled();
90+
consoleWarnSpy.mockRestore();
91+
});
92+
93+
it('emits a console.warn when Sentry is initialized with @sentry/node instead of @sentry/hono/node', () => {
94+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
95+
const fakeClient = {
96+
getOptions: () => ({
97+
debug: false,
98+
_metadata: { sdk: { name: 'sentry.javascript.node' } },
99+
}),
100+
};
101+
vi.spyOn(SentryCore, 'getClient').mockReturnValue(fakeClient as unknown as SentryCore.Client);
102+
103+
const app = new Hono();
104+
sentry(app);
105+
106+
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining('not initialized with `@sentry/hono/node`'));
107+
consoleWarnSpy.mockRestore();
108+
});
109+
110+
it('emits a console.warn when Sentry is initialized without any SDK metadata', () => {
111+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
77112
const fakeClient = { getOptions: () => ({ debug: false }) };
78113
vi.spyOn(SentryCore, 'getClient').mockReturnValue(fakeClient as unknown as SentryCore.Client);
79114

80115
const app = new Hono();
81116
sentry(app);
82117

83-
expect(warnSpy).not.toHaveBeenCalled();
118+
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining('not initialized with `@sentry/hono/node`'));
119+
consoleWarnSpy.mockRestore();
84120
});
85121
});
86122

@@ -135,26 +171,33 @@ describe('Hono Node Middleware', () => {
135171
expect(middleware.constructor.name).toBe('AsyncFunction');
136172
});
137173

138-
it('emits a warning when Sentry is not initialized', () => {
139-
const warnSpy = vi.spyOn(SentryCore.debug, 'warn');
174+
it('emits a console.warn when Sentry is not initialized', () => {
175+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
140176
vi.spyOn(SentryCore, 'getClient').mockReturnValue(undefined);
141177

142178
const app = new Hono();
143179
sentry(app);
144180

145-
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('Sentry is not initialized'));
181+
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining('Sentry is not initialized'));
182+
consoleWarnSpy.mockRestore();
146183
});
147184

148-
it('does not emit a warning when Sentry is already initialized', () => {
149-
const warnSpy = vi.spyOn(SentryCore.debug, 'warn');
150-
const fakeClient = { getOptions: () => ({ debug: false }) };
185+
it('does not emit a warning when Sentry is already initialized with @sentry/hono/node', () => {
186+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
187+
const fakeClient = {
188+
getOptions: () => ({
189+
debug: false,
190+
_metadata: { sdk: { name: 'sentry.javascript.hono' } },
191+
}),
192+
};
151193
vi.spyOn(SentryCore, 'getClient').mockReturnValue(fakeClient as unknown as SentryCore.Client);
152194

153195
const app = new Hono();
154196
const middleware = sentry(app);
155197

156-
expect(warnSpy).not.toHaveBeenCalled();
198+
expect(consoleWarnSpy).not.toHaveBeenCalled();
157199
expect(middleware.constructor.name).toBe('AsyncFunction');
200+
consoleWarnSpy.mockRestore();
158201
});
159202
});
160203

0 commit comments

Comments
 (0)