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 {
7+ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ,
8+ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ,
9+ captureException ,
10+ flush ,
11+ startSpan ,
12+ withIsolationScope ,
13+ } from '@sentry/core' ;
314import { setAsyncLocalStorageAsyncContextStrategy } from './async' ;
15+ import type { CloudflareOptions } from './client' ;
416import { wrapRequestHandler } from './request' ;
17+ import { addCloudResourceContext } from './scope-utils' ;
18+ import { init } from './sdk' ;
519
620/**
721 * Extract environment generic from exported handler.
@@ -21,7 +35,7 @@ type ExtractEnv<P> = P extends ExportedHandler<infer Env> ? Env : never;
2135 */
2236// eslint-disable-next-line @typescript-eslint/no-explicit-any
2337export function withSentry < E extends ExportedHandler < any > > (
24- optionsCallback : ( env : ExtractEnv < E > ) => Options ,
38+ optionsCallback : ( env : ExtractEnv < E > ) => CloudflareOptions ,
2539 handler : E ,
2640) : E {
2741 setAsyncLocalStorageAsyncContextStrategy ( ) ;
@@ -40,5 +54,52 @@ export function withSentry<E extends ExportedHandler<any>>(
4054 ( handler . fetch as any ) . __SENTRY_INSTRUMENTED__ = true ;
4155 }
4256
57+ if (
58+ 'scheduled' in handler &&
59+ typeof handler . scheduled === 'function' &&
60+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
61+ ! ( handler . scheduled as any ) . __SENTRY_INSTRUMENTED__
62+ ) {
63+ handler . scheduled = new Proxy ( handler . scheduled , {
64+ apply ( target , thisArg , args : Parameters < ExportedHandlerScheduledHandler < ExtractEnv < E > > > ) {
65+ const [ event , env , context ] = args ;
66+ return withIsolationScope ( isolationScope => {
67+ const options = optionsCallback ( env ) ;
68+ const client = init ( options ) ;
69+ isolationScope . setClient ( client ) ;
70+
71+ addCloudResourceContext ( isolationScope ) ;
72+
73+ return startSpan (
74+ {
75+ op : 'faas.cron' ,
76+ name : `Scheduled Cron ${ event . cron } ` ,
77+ attributes : {
78+ 'faas.cron' : event . cron ,
79+ 'faas.time' : new Date ( event . scheduledTime ) . toISOString ( ) ,
80+ 'faas.trigger' : 'timer' ,
81+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.faas.cloudflare' ,
82+ [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] : 'task' ,
83+ } ,
84+ } ,
85+ async ( ) => {
86+ try {
87+ return await ( target . apply ( thisArg , args ) as ReturnType < typeof target > ) ;
88+ } catch ( e ) {
89+ captureException ( e , { mechanism : { handled : false , type : 'cloudflare' } } ) ;
90+ throw e ;
91+ } finally {
92+ context . waitUntil ( flush ( 2000 ) ) ;
93+ }
94+ } ,
95+ ) ;
96+ } ) ;
97+ } ,
98+ } ) ;
99+
100+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
101+ ( handler . scheduled as any ) . __SENTRY_INSTRUMENTED__ = true ;
102+ }
103+
43104 return handler ;
44105}
0 commit comments