Skip to content

Commit 4eec1cf

Browse files
author
cod1k
committed
Introduce makeFlushAfterAll utility to streamline and customize Sentry's flush handling across Cloudflare handlers.
1 parent 797ebd1 commit 4eec1cf

File tree

3 files changed

+48
-8
lines changed

3 files changed

+48
-8
lines changed

packages/cloudflare/src/flush.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import type { ExecutionContext } from '@cloudflare/workers-types';
2+
import { flush } from '@sentry/core';
3+
4+
type Flusher = (...params: Parameters<typeof flush>) => void;
5+
6+
/**
7+
* Enhances the given execution context by wrapping its `waitUntil` method with a proxy
8+
* to monitor pending tasks, and provides a flusher function to ensure all tasks
9+
* have been completed before executing any subsequent logic.
10+
*
11+
* @param {ExecutionContext | void} context - The execution context to be enhanced. If no context is provided, the function returns undefined.
12+
* @return {Flusher | void} Returns a flusher function if a valid context is provided, otherwise undefined.
13+
*/
14+
export function makeFlushAfterAll(context: ExecutionContext): Flusher {
15+
let _resolve: () => void = () => undefined;
16+
const allDone = new Promise<void>(resolve => {
17+
_resolve = resolve;
18+
});
19+
let pending = 0;
20+
const originalWaitUntil = context.waitUntil.bind(context) as typeof context.waitUntil;
21+
context.waitUntil = promise => {
22+
pending++;
23+
return originalWaitUntil(
24+
promise.finally(() => {
25+
if (--pending === 0) _resolve();
26+
}),
27+
);
28+
};
29+
return (...params: Parameters<typeof flush>) => {
30+
if (pending === 0) {
31+
return originalWaitUntil(flush(...params));
32+
}
33+
originalWaitUntil(allDone.finally(() => flush(...params)));
34+
};
35+
}

packages/cloudflare/src/handler.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {
22
captureException,
3-
flush,
43
SEMANTIC_ATTRIBUTE_SENTRY_OP,
54
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
65
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
@@ -9,6 +8,7 @@ import {
98
} from '@sentry/core';
109
import { setAsyncLocalStorageAsyncContextStrategy } from './async';
1110
import type { CloudflareOptions } from './client';
11+
import { makeFlushAfterAll } from './flush';
1212
import { isInstrumented, markAsInstrumented } from './instrument';
1313
import { getFinalOptions } from './options';
1414
import { wrapRequestHandler } from './request';
@@ -72,6 +72,7 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
7272
handler.scheduled = new Proxy(handler.scheduled, {
7373
apply(target, thisArg, args: Parameters<ExportedHandlerScheduledHandler<Env>>) {
7474
const [event, env, context] = args;
75+
const flushAfterAll = makeFlushAfterAll(context);
7576
return withIsolationScope(isolationScope => {
7677
const options = getFinalOptions(optionsCallback(env), env);
7778

@@ -99,7 +100,7 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
99100
captureException(e, { mechanism: { handled: false, type: 'cloudflare' } });
100101
throw e;
101102
} finally {
102-
context.waitUntil(flush(2000));
103+
flushAfterAll(2000);
103104
}
104105
},
105106
);
@@ -114,6 +115,7 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
114115
handler.email = new Proxy(handler.email, {
115116
apply(target, thisArg, args: Parameters<EmailExportedHandler<Env>>) {
116117
const [emailMessage, env, context] = args;
118+
const flushAfterAll = makeFlushAfterAll(context);
117119
return withIsolationScope(isolationScope => {
118120
const options = getFinalOptions(optionsCallback(env), env);
119121

@@ -139,7 +141,7 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
139141
captureException(e, { mechanism: { handled: false, type: 'cloudflare' } });
140142
throw e;
141143
} finally {
142-
context.waitUntil(flush(2000));
144+
flushAfterAll(2000);
143145
}
144146
},
145147
);
@@ -154,6 +156,7 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
154156
handler.queue = new Proxy(handler.queue, {
155157
apply(target, thisArg, args: Parameters<ExportedHandlerQueueHandler<Env, QueueHandlerMessage>>) {
156158
const [batch, env, context] = args;
159+
const flushAfterAll = makeFlushAfterAll(context);
157160

158161
return withIsolationScope(isolationScope => {
159162
const options = getFinalOptions(optionsCallback(env), env);
@@ -185,7 +188,7 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
185188
captureException(e, { mechanism: { handled: false, type: 'cloudflare' } });
186189
throw e;
187190
} finally {
188-
context.waitUntil(flush(2000));
191+
flushAfterAll(2000);
189192
}
190193
},
191194
);
@@ -200,6 +203,7 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
200203
handler.tail = new Proxy(handler.tail, {
201204
apply(target, thisArg, args: Parameters<ExportedHandlerTailHandler<Env>>) {
202205
const [, env, context] = args;
206+
const flushAfterAll = makeFlushAfterAll(context);
203207

204208
return withIsolationScope(async isolationScope => {
205209
const options = getFinalOptions(optionsCallback(env), env);
@@ -215,7 +219,7 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
215219
captureException(e, { mechanism: { handled: false, type: 'cloudflare' } });
216220
throw e;
217221
} finally {
218-
context.waitUntil(flush(2000));
222+
flushAfterAll(2000);
219223
}
220224
});
221225
},

packages/cloudflare/src/request.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import type { ExecutionContext, IncomingRequestCfProperties } from '@cloudflare/
22
import {
33
captureException,
44
continueTrace,
5-
flush,
65
getHttpSpanDetailsFromUrlObject,
76
parseStringToURLObject,
87
SEMANTIC_ATTRIBUTE_SENTRY_OP,
@@ -11,6 +10,7 @@ import {
1110
withIsolationScope,
1211
} from '@sentry/core';
1312
import type { CloudflareOptions } from './client';
13+
import { makeFlushAfterAll } from './flush';
1414
import { addCloudResourceContext, addCultureContext, addRequest } from './scope-utils';
1515
import { init } from './sdk';
1616

@@ -34,6 +34,7 @@ export function wrapRequestHandler(
3434
// For example, for Astro while prerendering pages at build time.
3535
// see: https://github.com/getsentry/sentry-javascript/issues/13217
3636
const context = wrapperOptions.context as ExecutionContext | undefined;
37+
const afterAllFlusher = context ? makeFlushAfterAll(context) : undefined;
3738

3839
const client = init(options);
3940
isolationScope.setClient(client);
@@ -65,7 +66,7 @@ export function wrapRequestHandler(
6566
captureException(e, { mechanism: { handled: false, type: 'cloudflare' } });
6667
throw e;
6768
} finally {
68-
context?.waitUntil(flush(2000));
69+
afterAllFlusher?.(2000);
6970
}
7071
}
7172

@@ -89,7 +90,7 @@ export function wrapRequestHandler(
8990
captureException(e, { mechanism: { handled: false, type: 'cloudflare' } });
9091
throw e;
9192
} finally {
92-
context?.waitUntil(flush(2000));
93+
afterAllFlusher?.(2000);
9394
}
9495
},
9596
);

0 commit comments

Comments
 (0)