* Create and track xhr request spans * * @returns Span if a span was created, otherwise void.
( handlerData: HandlerDataXhr, shouldCreateSpan: (url: string) => boolean, shouldAttachHeaders: (url: string) => boolean, spans: Record<string, Span>, propagateTraceparent?: boolean, onRequestSpanEnd?: RequestInstrumentationOptions['onRequestSpanEnd'], )
| 326 | * @returns Span if a span was created, otherwise void. |
| 327 | */ |
| 328 | function xhrCallback( |
| 329 | handlerData: HandlerDataXhr, |
| 330 | shouldCreateSpan: (url: string) => boolean, |
| 331 | shouldAttachHeaders: (url: string) => boolean, |
| 332 | spans: Record<string, Span>, |
| 333 | propagateTraceparent?: boolean, |
| 334 | onRequestSpanEnd?: RequestInstrumentationOptions['onRequestSpanEnd'], |
| 335 | ): Span | undefined { |
| 336 | const xhr = handlerData.xhr; |
| 337 | const sentryXhrData = xhr?.[SENTRY_XHR_DATA_KEY]; |
| 338 | |
| 339 | if (!xhr || xhr.__sentry_own_request__ || !sentryXhrData) { |
| 340 | return undefined; |
| 341 | } |
| 342 | |
| 343 | const { url, method } = sentryXhrData; |
| 344 | |
| 345 | const shouldCreateSpanResult = hasSpansEnabled() && shouldCreateSpan(url); |
| 346 | |
| 347 | // Handle XHR completion - clean up spans from the record |
| 348 | if (handlerData.endTimestamp) { |
| 349 | const spanId = xhr.__sentry_xhr_span_id__; |
| 350 | if (!spanId) return; |
| 351 | |
| 352 | const span = spans[spanId]; |
| 353 | |
| 354 | if (span) { |
| 355 | if (shouldCreateSpanResult && sentryXhrData.status_code !== undefined) { |
| 356 | setHttpStatus(span, sentryXhrData.status_code); |
| 357 | span.end(); |
| 358 | |
| 359 | onRequestSpanEnd?.(span, { |
| 360 | headers: createHeadersSafely(parseXhrResponseHeaders(xhr as XMLHttpRequest & SentryWrappedXMLHttpRequest)), |
| 361 | error: handlerData.error, |
| 362 | }); |
| 363 | } |
| 364 | |
| 365 | // eslint-disable-next-line @typescript-eslint/no-dynamic-delete |
| 366 | delete spans[spanId]; |
| 367 | } |
| 368 | |
| 369 | return undefined; |
| 370 | } |
| 371 | |
| 372 | const fullUrl = getFullURL(url); |
| 373 | const parsedUrl = fullUrl ? parseUrl(fullUrl) : parseUrl(url); |
| 374 | const sanitizedFullUrl = fullUrl ? stripDataUrlContent(fullUrl) : undefined; |
| 375 | |
| 376 | const urlForSpanName = stripDataUrlContent(stripUrlQueryAndFragment(url)); |
| 377 | |
| 378 | const client = getClient(); |
| 379 | const hasParent = !!getActiveSpan(); |
| 380 | // With span streaming, we always emit http.client spans, even without a parent span |
| 381 | const shouldEmitSpan = hasParent || (!!client && hasSpanStreamingEnabled(client)); |
| 382 | |
| 383 | const span = |
| 384 | shouldCreateSpanResult && shouldEmitSpan |
| 385 | ? startInactiveSpan({ |
no test coverage detected