11import {
22 captureException ,
33 flush ,
4+ SEMANTIC_ATTRIBUTE_SENTRY_OP ,
45 SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ,
56 SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ,
67 startSpan ,
@@ -66,7 +67,7 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
6667 'faas.cron' : event . cron ,
6768 'faas.time' : new Date ( event . scheduledTime ) . toISOString ( ) ,
6869 'faas.trigger' : 'timer' ,
69- [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.faas.cloudflare' ,
70+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.faas.cloudflare.scheduled ' ,
7071 [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] : 'task' ,
7172 } ,
7273 } ,
@@ -87,8 +88,122 @@ export function withSentry<Env = unknown, QueueHandlerMessage = unknown, CfHostM
8788
8889 markAsInstrumented ( handler . scheduled ) ;
8990 }
91+
92+ if ( 'email' in handler && typeof handler . email === 'function' && ! isInstrumented ( handler . email ) ) {
93+ handler . email = new Proxy ( handler . email , {
94+ apply ( target , thisArg , args : Parameters < EmailExportedHandler < Env > > ) {
95+ const [ emailMessage , env , context ] = args ;
96+ return withIsolationScope ( isolationScope => {
97+ const options = getFinalOptions ( optionsCallback ( env ) , env ) ;
98+
99+ const client = init ( options ) ;
100+ isolationScope . setClient ( client ) ;
101+
102+ addCloudResourceContext ( isolationScope ) ;
103+
104+ return startSpan (
105+ {
106+ op : 'faas.email' ,
107+ name : `Handle Email ${ emailMessage . to } ` ,
108+ attributes : {
109+ 'faas.trigger' : 'email' ,
110+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.faas.cloudflare.email' ,
111+ [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] : 'task' ,
112+ } ,
113+ } ,
114+ async ( ) => {
115+ try {
116+ return await ( target . apply ( thisArg , args ) as ReturnType < typeof target > ) ;
117+ } catch ( e ) {
118+ captureException ( e , { mechanism : { handled : false , type : 'cloudflare' } } ) ;
119+ throw e ;
120+ } finally {
121+ context . waitUntil ( flush ( 2000 ) ) ;
122+ }
123+ } ,
124+ ) ;
125+ } ) ;
126+ } ,
127+ } ) ;
128+
129+ markAsInstrumented ( handler . email ) ;
130+ }
131+
132+ if ( 'queue' in handler && typeof handler . queue === 'function' && ! isInstrumented ( handler . queue ) ) {
133+ handler . queue = new Proxy ( handler . queue , {
134+ apply ( target , thisArg , args : Parameters < ExportedHandlerQueueHandler < Env , QueueHandlerMessage > > ) {
135+ const [ batch , env , context ] = args ;
136+
137+ return withIsolationScope ( isolationScope => {
138+ const options = getFinalOptions ( optionsCallback ( env ) , env ) ;
139+
140+ const client = init ( options ) ;
141+ isolationScope . setClient ( client ) ;
142+
143+ addCloudResourceContext ( isolationScope ) ;
144+
145+ return startSpan (
146+ {
147+ op : 'faas.queue' ,
148+ name : `process ${ batch . queue } ` ,
149+ attributes : {
150+ 'faas.trigger' : 'pubsub' ,
151+ 'messaging.destination.name' : batch . queue ,
152+ 'messaging.system' : 'cloudflare' ,
153+ 'messaging.batch.message_count' : batch . messages . length ,
154+ 'messaging.message.retry.count' : batch . messages . reduce ( ( acc , message ) => acc + message . attempts , 0 ) ,
155+ [ SEMANTIC_ATTRIBUTE_SENTRY_OP ] : 'queue.process' ,
156+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.faas.cloudflare.queue' ,
157+ [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] : 'task' ,
158+ } ,
159+ } ,
160+ async ( ) => {
161+ try {
162+ return await ( target . apply ( thisArg , args ) as ReturnType < typeof target > ) ;
163+ } catch ( e ) {
164+ captureException ( e , { mechanism : { handled : false , type : 'cloudflare' } } ) ;
165+ throw e ;
166+ } finally {
167+ context . waitUntil ( flush ( 2000 ) ) ;
168+ }
169+ } ,
170+ ) ;
171+ } ) ;
172+ } ,
173+ } ) ;
174+
175+ markAsInstrumented ( handler . queue ) ;
176+ }
177+
178+ if ( 'tail' in handler && typeof handler . tail === 'function' && ! isInstrumented ( handler . tail ) ) {
179+ handler . tail = new Proxy ( handler . tail , {
180+ apply ( target , thisArg , args : Parameters < ExportedHandlerTailHandler < Env > > ) {
181+ const [ , env , context ] = args ;
182+
183+ return withIsolationScope ( async isolationScope => {
184+ const options = getFinalOptions ( optionsCallback ( env ) , env ) ;
185+
186+ const client = init ( options ) ;
187+ isolationScope . setClient ( client ) ;
188+
189+ addCloudResourceContext ( isolationScope ) ;
190+
191+ try {
192+ return await ( target . apply ( thisArg , args ) as ReturnType < typeof target > ) ;
193+ } catch ( e ) {
194+ captureException ( e , { mechanism : { handled : false , type : 'cloudflare' } } ) ;
195+ throw e ;
196+ } finally {
197+ context . waitUntil ( flush ( 2000 ) ) ;
198+ }
199+ } ) ;
200+ } ,
201+ } ) ;
202+
203+ markAsInstrumented ( handler . tail ) ;
204+ }
205+
90206 // This is here because Miniflare sometimes cannot get instrumented
91- //
92207 } catch ( e ) {
93208 // Do not console anything here, we don't want to spam the console with errors
94209 }
0 commit comments