Skip to content

Commit f9ace4a

Browse files
committed
add Hono integration
1 parent bbb1324 commit f9ace4a

File tree

5 files changed

+78
-2
lines changed

5 files changed

+78
-2
lines changed

packages/cloudflare/src/handler.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import { setAsyncLocalStorageAsyncContextStrategy } from './async';
1111
import type { CloudflareOptions } from './client';
1212
import { isInstrumented, markAsInstrumented } from './instrument';
13+
import { getHonoIntegration } from './integrations/hono';
1314
import { getFinalOptions } from './options';
1415
import { wrapRequestHandler } from './request';
1516
import { addCloudResourceContext } from './scope-utils';
@@ -48,7 +49,7 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
4849
markAsInstrumented(handler.fetch);
4950
}
5051

51-
/* hono does not reach the catch block of the fetch handler and captureException needs to be called in the hono errorHandler */
52+
/* Hono does not reach the catch block of the fetch handler and captureException needs to be called in the hono errorHandler */
5253
if (
5354
'onError' in handler &&
5455
'errorHandler' in handler &&
@@ -59,7 +60,7 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
5960
apply(target, thisArg, args) {
6061
const [err] = args;
6162

62-
captureException(err, { mechanism: { handled: false, type: 'auto.faas.cloudflare.error_handler' } });
63+
getHonoIntegration()?.handleHonoException(err);
6364

6465
return Reflect.apply(target, thisArg, args);
6566
},

packages/cloudflare/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export { getDefaultIntegrations } from './sdk';
111111

112112
export { fetchIntegration } from './integrations/fetch';
113113
export { vercelAIIntegration } from './integrations/tracing/vercelai';
114+
export { honoIntegration } from './integrations/hono';
114115

115116
export { instrumentD1WithSentry } from './d1';
116117

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import type { IntegrationFn } from '@sentry/core';
2+
import { captureException, debug, defineIntegration, getClient } from '@sentry/core';
3+
import { DEBUG_BUILD } from '../debug-build';
4+
5+
const INTEGRATION_NAME = 'Hono';
6+
7+
interface HonoError extends Error {
8+
status?: number;
9+
}
10+
11+
export interface Options {
12+
/**
13+
* Callback method deciding whether error should be captured and sent to Sentry
14+
* @param error Captured middleware error
15+
*/
16+
shouldHandleError?(this: void, error: HonoError): boolean;
17+
}
18+
19+
/** Only exported for internal use */
20+
export function getHonoIntegration(): ReturnType<typeof _honoIntegration> | undefined {
21+
const client = getClient();
22+
if (!client) {
23+
return undefined;
24+
} else {
25+
return client.getIntegrationByName(_honoIntegration.name);
26+
}
27+
}
28+
29+
// todo: implement this
30+
function isHonoError(err: unknown): err is HonoError {
31+
// @ts-ignore
32+
return 'status' in err;
33+
}
34+
35+
const _honoIntegration = ((options: Partial<Options> = {}) => {
36+
let _shouldHandleError: (error: Error) => boolean;
37+
38+
return {
39+
name: INTEGRATION_NAME,
40+
setupOnce() {
41+
_shouldHandleError = options.shouldHandleError || defaultShouldHandleError;
42+
},
43+
handleHonoException(err: HonoError): void {
44+
if (!isHonoError) {
45+
DEBUG_BUILD && debug.log('Hono integration could not detect a Hono error');
46+
return;
47+
}
48+
if (_shouldHandleError(err)) {
49+
captureException(err, { mechanism: { handled: false, type: 'auto.faas.cloudflare.error_handler' } });
50+
}
51+
},
52+
};
53+
}) satisfies IntegrationFn;
54+
55+
/**
56+
* Automatically captures exceptions caught with the `onError` handler in Hono.
57+
*
58+
* The integration is added by default.
59+
*/
60+
export const honoIntegration = defineIntegration(_honoIntegration);
61+
62+
/**
63+
* Default function to determine if an error should be sent to Sentry
64+
*
65+
* 3xx and 4xx errors are not sent by default.
66+
*/
67+
function defaultShouldHandleError(error: HonoError): boolean {
68+
// todo: add test for checking error without status
69+
const statusCode = error?.status;
70+
// 3xx and 4xx errors are not sent by default.
71+
return statusCode ? statusCode >= 500 || statusCode <= 299 : true;
72+
}

packages/cloudflare/src/sdk.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type { CloudflareClientOptions, CloudflareOptions } from './client';
1414
import { CloudflareClient } from './client';
1515
import { makeFlushLock } from './flush';
1616
import { fetchIntegration } from './integrations/fetch';
17+
import { honoIntegration } from './integrations/hono';
1718
import { setupOpenTelemetryTracer } from './opentelemetry/tracer';
1819
import { makeCloudflareTransport } from './transport';
1920
import { defaultStackParser } from './vendor/stacktrace';
@@ -31,6 +32,7 @@ export function getDefaultIntegrations(options: CloudflareOptions): Integration[
3132
functionToStringIntegration(),
3233
linkedErrorsIntegration(),
3334
fetchIntegration(),
35+
honoIntegration(),
3436
// TODO(v11): the `include` object should be defined directly in the integration based on `sendDefaultPii`
3537
requestDataIntegration(sendDefaultPii ? undefined : { include: { cookies: false } }),
3638
consoleIntegration(),

packages/cloudflare/test/integrations/hono.test.ts

Whitespace-only changes.

0 commit comments

Comments
 (0)