1
1
/* eslint-disable max-lines */
2
- import { getCurrentHub , hasTracingEnabled } from '@sentry/core' ;
3
- import type { DynamicSamplingContext , Span } from '@sentry/types' ;
2
+ import { getCurrentHub , getDynamicSamplingContextFromClient , hasTracingEnabled } from '@sentry/core' ;
3
+ import type { Client , Scope , Span } from '@sentry/types' ;
4
4
import {
5
5
addInstrumentationHandler ,
6
6
BAGGAGE_HEADER_NAME ,
7
7
browserPerformanceTimeOrigin ,
8
8
dynamicSamplingContextToSentryBaggageHeader ,
9
+ generateSentryTraceHeader ,
9
10
isInstanceOf ,
10
11
SENTRY_XHR_DATA_KEY ,
11
12
stringMatchesSomePattern ,
@@ -219,12 +220,14 @@ export function fetchCallback(
219
220
shouldCreateSpan : ( url : string ) => boolean ,
220
221
shouldAttachHeaders : ( url : string ) => boolean ,
221
222
spans : Record < string , Span > ,
222
- ) : Span | void {
223
- if ( ! hasTracingEnabled ( ) || ! ( handlerData . fetchData && shouldCreateSpan ( handlerData . fetchData . url ) ) ) {
224
- return ;
223
+ ) : Span | undefined {
224
+ if ( ! hasTracingEnabled ( ) || ! handlerData . fetchData ) {
225
+ return undefined ;
225
226
}
226
227
227
- if ( handlerData . endTimestamp ) {
228
+ const shouldCreateSpanResult = shouldCreateSpan ( handlerData . fetchData . url ) ;
229
+
230
+ if ( handlerData . endTimestamp && shouldCreateSpanResult ) {
228
231
const spanId = handlerData . fetchData . __span ;
229
232
if ( ! spanId ) return ;
230
233
@@ -251,27 +254,35 @@ export function fetchCallback(
251
254
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
252
255
delete spans [ spanId ] ;
253
256
}
254
- return ;
257
+ return undefined ;
255
258
}
256
259
257
- const currentSpan = getCurrentHub ( ) . getScope ( ) . getSpan ( ) ;
258
- const activeTransaction = currentSpan && currentSpan . transaction ;
259
-
260
- if ( currentSpan && activeTransaction ) {
261
- const { method, url } = handlerData . fetchData ;
262
- const span = currentSpan . startChild ( {
263
- data : {
264
- url,
265
- type : 'fetch' ,
266
- 'http.method' : method ,
267
- } ,
268
- description : `${ method } ${ url } ` ,
269
- op : 'http.client' ,
270
- } ) ;
271
-
260
+ const hub = getCurrentHub ( ) ;
261
+ const scope = hub . getScope ( ) ;
262
+ const client = hub . getClient ( ) ;
263
+ const parentSpan = scope . getSpan ( ) ;
264
+
265
+ const { method, url } = handlerData . fetchData ;
266
+
267
+ const span =
268
+ shouldCreateSpanResult && parentSpan
269
+ ? parentSpan . startChild ( {
270
+ data : {
271
+ url,
272
+ type : 'fetch' ,
273
+ 'http.method' : method ,
274
+ } ,
275
+ description : `${ method } ${ url } ` ,
276
+ op : 'http.client' ,
277
+ } )
278
+ : undefined ;
279
+
280
+ if ( span ) {
272
281
handlerData . fetchData . __span = span . spanId ;
273
282
spans [ span . spanId ] = span ;
283
+ }
274
284
285
+ if ( shouldAttachHeaders ( handlerData . fetchData . url ) && client ) {
275
286
const request : string | Request = handlerData . args [ 0 ] ;
276
287
277
288
// In case the user hasn't set the second argument of a fetch call we default it to `{}`.
@@ -280,35 +291,42 @@ export function fetchCallback(
280
291
// eslint-disable-next-line @typescript-eslint/no-explicit-any
281
292
const options : { [ key : string ] : any } = handlerData . args [ 1 ] ;
282
293
283
- if ( shouldAttachHeaders ( handlerData . fetchData . url ) ) {
284
- options . headers = addTracingHeadersToFetchRequest (
285
- request ,
286
- activeTransaction . getDynamicSamplingContext ( ) ,
287
- span ,
288
- options ,
289
- ) ;
290
- }
291
- return span ;
294
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
295
+ options . headers = addTracingHeadersToFetchRequest ( request , client , scope , options ) ;
292
296
}
297
+
298
+ return span ;
293
299
}
294
300
295
301
/**
296
302
* Adds sentry-trace and baggage headers to the various forms of fetch headers
297
303
*/
298
304
export function addTracingHeadersToFetchRequest (
299
305
request : string | unknown , // unknown is actually type Request but we can't export DOM types from this package,
300
- dynamicSamplingContext : Partial < DynamicSamplingContext > ,
301
- span : Span ,
306
+ client : Client ,
307
+ scope : Scope ,
302
308
options : {
303
309
headers ?:
304
310
| {
305
311
[ key : string ] : string [ ] | string | undefined ;
306
312
}
307
313
| PolymorphicRequestHeaders ;
308
314
} ,
309
- ) : PolymorphicRequestHeaders {
315
+ ) : PolymorphicRequestHeaders | undefined {
316
+ const span = scope . getSpan ( ) ;
317
+
318
+ const transaction = span && span . transaction ;
319
+
320
+ const { traceId, sampled, dsc } = scope . getPropagationContext ( ) ;
321
+
322
+ const sentryTraceHeader = span ? span . toTraceparent ( ) : generateSentryTraceHeader ( traceId , undefined , sampled ) ;
323
+ const dynamicSamplingContext = transaction
324
+ ? transaction . getDynamicSamplingContext ( )
325
+ : dsc
326
+ ? dsc
327
+ : getDynamicSamplingContextFromClient ( traceId , client , scope ) ;
328
+
310
329
const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader ( dynamicSamplingContext ) ;
311
- const sentryTraceHeader = span . toTraceparent ( ) ;
312
330
313
331
const headers =
314
332
typeof Request !== 'undefined' && isInstanceOf ( request , Request ) ? ( request as Request ) . headers : options . headers ;
@@ -364,25 +382,24 @@ export function addTracingHeadersToFetchRequest(
364
382
*
365
383
* @returns Span if a span was created, otherwise void.
366
384
*/
385
+ // eslint-disable-next-line complexity
367
386
export function xhrCallback (
368
387
handlerData : XHRData ,
369
388
shouldCreateSpan : ( url : string ) => boolean ,
370
389
shouldAttachHeaders : ( url : string ) => boolean ,
371
390
spans : Record < string , Span > ,
372
- ) : Span | void {
391
+ ) : Span | undefined {
373
392
const xhr = handlerData . xhr ;
374
393
const sentryXhrData = xhr && xhr [ SENTRY_XHR_DATA_KEY ] ;
375
394
376
- if (
377
- ! hasTracingEnabled ( ) ||
378
- ( xhr && xhr . __sentry_own_request__ ) ||
379
- ! ( xhr && sentryXhrData && shouldCreateSpan ( sentryXhrData . url ) )
380
- ) {
381
- return ;
395
+ if ( ! hasTracingEnabled ( ) || ( xhr && xhr . __sentry_own_request__ ) || ! xhr || ! sentryXhrData ) {
396
+ return undefined ;
382
397
}
383
398
399
+ const shouldCreateSpanResult = shouldCreateSpan ( sentryXhrData . url ) ;
400
+
384
401
// check first if the request has finished and is tracked by an existing span which should now end
385
- if ( handlerData . endTimestamp ) {
402
+ if ( handlerData . endTimestamp && shouldCreateSpanResult ) {
386
403
const spanId = xhr . __sentry_xhr_span_id__ ;
387
404
if ( ! spanId ) return ;
388
405
@@ -394,45 +411,68 @@ export function xhrCallback(
394
411
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
395
412
delete spans [ spanId ] ;
396
413
}
397
- return ;
414
+ return undefined ;
398
415
}
399
416
400
- const currentSpan = getCurrentHub ( ) . getScope ( ) . getSpan ( ) ;
401
- const activeTransaction = currentSpan && currentSpan . transaction ;
402
-
403
- if ( currentSpan && activeTransaction ) {
404
- const span = currentSpan . startChild ( {
405
- data : {
406
- ...sentryXhrData . data ,
407
- type : 'xhr' ,
408
- 'http.method' : sentryXhrData . method ,
409
- url : sentryXhrData . url ,
410
- } ,
411
- description : `${ sentryXhrData . method } ${ sentryXhrData . url } ` ,
412
- op : 'http.client' ,
413
- } ) ;
414
-
417
+ const hub = getCurrentHub ( ) ;
418
+ const scope = hub . getScope ( ) ;
419
+ const parentSpan = scope . getSpan ( ) ;
420
+
421
+ const span =
422
+ shouldCreateSpanResult && parentSpan
423
+ ? parentSpan . startChild ( {
424
+ data : {
425
+ ...sentryXhrData . data ,
426
+ type : 'xhr' ,
427
+ 'http.method' : sentryXhrData . method ,
428
+ url : sentryXhrData . url ,
429
+ } ,
430
+ description : `${ sentryXhrData . method } ${ sentryXhrData . url } ` ,
431
+ op : 'http.client' ,
432
+ } )
433
+ : undefined ;
434
+
435
+ if ( span ) {
415
436
xhr . __sentry_xhr_span_id__ = span . spanId ;
416
437
spans [ xhr . __sentry_xhr_span_id__ ] = span ;
438
+ }
417
439
418
- if ( xhr . setRequestHeader && shouldAttachHeaders ( sentryXhrData . url ) ) {
419
- try {
420
- xhr . setRequestHeader ( 'sentry-trace' , span . toTraceparent ( ) ) ;
440
+ if ( xhr . setRequestHeader && shouldAttachHeaders ( sentryXhrData . url ) ) {
441
+ if ( span ) {
442
+ const transaction = span && span . transaction ;
443
+ const dynamicSamplingContext = transaction && transaction . getDynamicSamplingContext ( ) ;
444
+ const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader ( dynamicSamplingContext ) ;
445
+ setHeaderOnXhr ( xhr , span . toTraceparent ( ) , sentryBaggageHeader ) ;
446
+ } else {
447
+ const client = hub . getClient ( ) ;
448
+ const { traceId, sampled, dsc } = scope . getPropagationContext ( ) ;
449
+ const sentryTraceHeader = generateSentryTraceHeader ( traceId , undefined , sampled ) ;
450
+ const dynamicSamplingContext =
451
+ dsc || ( client ? getDynamicSamplingContextFromClient ( traceId , client , scope ) : undefined ) ;
452
+ const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader ( dynamicSamplingContext ) ;
453
+ setHeaderOnXhr ( xhr , sentryTraceHeader , sentryBaggageHeader ) ;
454
+ }
455
+ }
421
456
422
- const dynamicSamplingContext = activeTransaction . getDynamicSamplingContext ( ) ;
423
- const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader ( dynamicSamplingContext ) ;
457
+ return span ;
458
+ }
424
459
425
- if ( sentryBaggageHeader ) {
426
- // From MDN: "If this method is called several times with the same header, the values are merged into one single request header."
427
- // We can therefore simply set a baggage header without checking what was there before
428
- // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader
429
- xhr . setRequestHeader ( BAGGAGE_HEADER_NAME , sentryBaggageHeader ) ;
430
- }
431
- } catch ( _ ) {
432
- // Error: InvalidStateError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED.
433
- }
460
+ function setHeaderOnXhr (
461
+ xhr : NonNullable < XHRData [ 'xhr' ] > ,
462
+ sentryTraceHeader : string ,
463
+ sentryBaggageHeader : string | undefined ,
464
+ ) : void {
465
+ try {
466
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
467
+ xhr . setRequestHeader ! ( 'sentry-trace' , sentryTraceHeader ) ;
468
+ if ( sentryBaggageHeader ) {
469
+ // From MDN: "If this method is called several times with the same header, the values are merged into one single request header."
470
+ // We can therefore simply set a baggage header without checking what was there before
471
+ // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader
472
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
473
+ xhr . setRequestHeader ! ( BAGGAGE_HEADER_NAME , sentryBaggageHeader ) ;
434
474
}
435
-
436
- return span ;
475
+ } catch ( _ ) {
476
+ // Error: InvalidStateError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED.
437
477
}
438
478
}
0 commit comments