Skip to content

Commit c309ac4

Browse files
authored
ref(cloudflare): Prepare for WorkerEntrypoint (#19742)
1 parent 5b2a92e commit c309ac4

File tree

15 files changed

+1814
-1516
lines changed

15 files changed

+1814
-1516
lines changed

packages/cloudflare/src/handler.ts

Lines changed: 0 additions & 261 deletions
This file was deleted.

packages/cloudflare/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export {
108108
instrumentLangGraph,
109109
} from '@sentry/core';
110110

111-
export { withSentry } from './handler';
111+
export { withSentry } from './withSentry';
112112
export { instrumentDurableObjectWithSentry } from './durableobject';
113113
export { sentryPagesPlugin } from './pages-plugin';
114114

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import type { EmailMessage, ExportedHandler } from '@cloudflare/workers-types';
2+
import {
3+
captureException,
4+
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
5+
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
6+
startSpan,
7+
withIsolationScope,
8+
} from '@sentry/core';
9+
import type { CloudflareOptions } from '../../client';
10+
import { flushAndDispose } from '../../flush';
11+
import { isInstrumented, markAsInstrumented } from '../../instrument';
12+
import { getFinalOptions } from '../../options';
13+
import { addCloudResourceContext } from '../../scope-utils';
14+
import { init } from '../../sdk';
15+
import { instrumentContext } from '../../utils/instrumentContext';
16+
17+
/**
18+
* Core email handler logic - wraps execution with Sentry instrumentation.
19+
*/
20+
function wrapEmailHandler(
21+
emailMessage: EmailMessage,
22+
options: CloudflareOptions,
23+
context: ExecutionContext,
24+
fn: () => unknown,
25+
): unknown {
26+
return withIsolationScope(isolationScope => {
27+
const waitUntil = context.waitUntil.bind(context);
28+
29+
const client = init({ ...options, ctx: context });
30+
isolationScope.setClient(client);
31+
32+
addCloudResourceContext(isolationScope);
33+
34+
return startSpan(
35+
{
36+
op: 'faas.email',
37+
name: `Handle Email ${emailMessage.to}`,
38+
attributes: {
39+
'faas.trigger': 'email',
40+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.email',
41+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'task',
42+
},
43+
},
44+
async () => {
45+
try {
46+
return await fn();
47+
} catch (e) {
48+
captureException(e, { mechanism: { handled: false, type: 'auto.faas.cloudflare.email' } });
49+
throw e;
50+
} finally {
51+
waitUntil(flushAndDispose(client));
52+
}
53+
},
54+
);
55+
});
56+
}
57+
58+
/**
59+
* Instruments an email handler for ExportedHandler (env/ctx come from args).
60+
*/
61+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
62+
export function instrumentExportedHandlerEmail<T extends ExportedHandler<any, any, any>>(
63+
handler: T,
64+
optionsCallback: (env: Parameters<NonNullable<T['email']>>[1]) => CloudflareOptions | undefined,
65+
): void {
66+
if (!('email' in handler) || typeof handler.email !== 'function' || isInstrumented(handler.email)) {
67+
return;
68+
}
69+
70+
handler.email = new Proxy(handler.email, {
71+
apply(target, thisArg, args: Parameters<NonNullable<T['email']>>) {
72+
const [emailMessage, env, ctx] = args;
73+
const context = instrumentContext(ctx);
74+
args[2] = context;
75+
76+
const options = getFinalOptions(optionsCallback(env), env);
77+
78+
return wrapEmailHandler(emailMessage, options, context, () => target.apply(thisArg, args));
79+
},
80+
});
81+
82+
markAsInstrumented(handler.email);
83+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { ExportedHandler } from '@cloudflare/workers-types';
2+
import type { CloudflareOptions } from '../../client';
3+
import { isInstrumented, markAsInstrumented } from '../../instrument';
4+
import { getFinalOptions } from '../../options';
5+
import { wrapRequestHandler } from '../../request';
6+
import { instrumentContext } from '../../utils/instrumentContext';
7+
8+
/**
9+
* Instruments a fetch handler for ExportedHandler (env/ctx come from args).
10+
*/
11+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
12+
export function instrumentExportedHandlerFetch<T extends ExportedHandler<any, any, any>>(
13+
handler: T,
14+
optionsCallback: (env: Parameters<NonNullable<T['fetch']>>[1]) => CloudflareOptions | undefined,
15+
): void {
16+
if (!('fetch' in handler) || typeof handler.fetch !== 'function' || isInstrumented(handler.fetch)) {
17+
return;
18+
}
19+
20+
handler.fetch = new Proxy(handler.fetch, {
21+
apply(target, thisArg, args: Parameters<NonNullable<T['fetch']>>) {
22+
const [request, env, ctx] = args;
23+
const context = instrumentContext(ctx);
24+
args[2] = context;
25+
26+
const options = getFinalOptions(optionsCallback(env), env);
27+
28+
return wrapRequestHandler({ options, request, context }, () => target.apply(thisArg, args));
29+
},
30+
});
31+
32+
markAsInstrumented(handler.fetch);
33+
}

0 commit comments

Comments
 (0)