1- import type { ExportedHandler , ExportedHandlerFetchHandler } from '@cloudflare/workers-types' ;
2- import type { Options } from '@sentry/types' ;
1+ import type {
2+ ExportedHandler ,
3+ ExportedHandlerFetchHandler ,
4+ ExportedHandlerScheduledHandler ,
5+ } from '@cloudflare/workers-types' ;
6+ import { captureException , flush , startSpan , withIsolationScope } from '@sentry/core' ;
37import { setAsyncLocalStorageAsyncContextStrategy } from './async' ;
8+ import type { CloudflareOptions } from './client' ;
49import { wrapRequestHandler } from './request' ;
10+ import { addCloudResourceContext } from './scope-utils' ;
11+ import { init } from './sdk' ;
512
613/**
714 * Extract environment generic from exported handler.
@@ -21,7 +28,7 @@ type ExtractEnv<P> = P extends ExportedHandler<infer Env> ? Env : never;
2128 */
2229// eslint-disable-next-line @typescript-eslint/no-explicit-any
2330export function withSentry < E extends ExportedHandler < any > > (
24- optionsCallback : ( env : ExtractEnv < E > ) => Options ,
31+ optionsCallback : ( env : ExtractEnv < E > ) => CloudflareOptions ,
2532 handler : E ,
2633) : E {
2734 setAsyncLocalStorageAsyncContextStrategy ( ) ;
@@ -40,5 +47,50 @@ export function withSentry<E extends ExportedHandler<any>>(
4047 ( handler . fetch as any ) . __SENTRY_INSTRUMENTED__ = true ;
4148 }
4249
50+ if (
51+ 'scheduled' in handler &&
52+ typeof handler . scheduled === 'function' &&
53+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
54+ ! ( handler . scheduled as any ) . __SENTRY_INSTRUMENTED__
55+ ) {
56+ handler . scheduled = new Proxy ( handler . scheduled , {
57+ apply ( target , thisArg , args : Parameters < ExportedHandlerScheduledHandler < ExtractEnv < E > > > ) {
58+ const [ event , env , context ] = args ;
59+ return withIsolationScope ( isolationScope => {
60+ const options = optionsCallback ( env ) ;
61+ const client = init ( options ) ;
62+ isolationScope . setClient ( client ) ;
63+
64+ addCloudResourceContext ( isolationScope ) ;
65+
66+ return startSpan (
67+ {
68+ op : 'faas.cron' ,
69+ name : `Scheduled Cron ${ event . cron } ` ,
70+ attributes : {
71+ 'faas.cron' : event . cron ,
72+ 'faas.time' : new Date ( event . scheduledTime ) . toISOString ( ) ,
73+ 'faas.trigger' : 'timer' ,
74+ } ,
75+ } ,
76+ async ( ) => {
77+ try {
78+ return await ( target . apply ( thisArg , args ) as ReturnType < typeof target > ) ;
79+ } catch ( e ) {
80+ captureException ( e , { mechanism : { handled : false , type : 'cloudflare' } } ) ;
81+ throw e ;
82+ } finally {
83+ context . waitUntil ( flush ( 2000 ) ) ;
84+ }
85+ } ,
86+ ) ;
87+ } ) ;
88+ } ,
89+ } ) ;
90+
91+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
92+ ( handler . scheduled as any ) . __SENTRY_INSTRUMENTED__ = true ;
93+ }
94+
4395 return handler ;
4496}
0 commit comments