Skip to content

Commit 1e2bf6e

Browse files
author
Luca Forstner
authored
ref(tracing-internal): Export fetch instrumentation (#9473)
1 parent f48b697 commit 1e2bf6e

File tree

6 files changed

+227
-239
lines changed

6 files changed

+227
-239
lines changed

packages/tracing-internal/src/browser/index.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ export * from '../exports';
33
export type { RequestInstrumentationOptions } from './request';
44

55
export { BrowserTracing, BROWSER_TRACING_INTEGRATION_ID } from './browsertracing';
6-
export {
7-
instrumentOutgoingRequests,
8-
defaultRequestInstrumentationOptions,
9-
addTracingHeadersToFetchRequest,
10-
} from './request';
6+
export { instrumentOutgoingRequests, defaultRequestInstrumentationOptions } from './request';
117

128
export {
139
addPerformanceInstrumentationHandler,

packages/tracing-internal/src/browser/request.ts

Lines changed: 4 additions & 204 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
/* eslint-disable max-lines */
22
import { getCurrentHub, getDynamicSamplingContextFromClient, hasTracingEnabled } from '@sentry/core';
3-
import type { Client, Scope, Span } from '@sentry/types';
3+
import type { HandlerDataFetch, Span } from '@sentry/types';
44
import {
55
addInstrumentationHandler,
66
BAGGAGE_HEADER_NAME,
77
browserPerformanceTimeOrigin,
88
dynamicSamplingContextToSentryBaggageHeader,
99
generateSentryTraceHeader,
10-
isInstanceOf,
1110
SENTRY_XHR_DATA_KEY,
1211
stringMatchesSomePattern,
1312
} from '@sentry/utils';
1413

14+
import { instrumentFetchRequest } from '../common/fetch';
1515
import { addPerformanceInstrumentationHandler } from './instrument';
1616

1717
export const DEFAULT_TRACE_PROPAGATION_TARGETS = ['localhost', /^\/(?!\/)/];
@@ -66,26 +66,6 @@ export interface RequestInstrumentationOptions {
6666
shouldCreateSpanForRequest?(this: void, url: string): boolean;
6767
}
6868

69-
/** Data returned from fetch callback */
70-
export interface FetchData {
71-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
72-
args: any[]; // the arguments passed to the fetch call itself
73-
fetchData?: {
74-
method: string;
75-
url: string;
76-
// span_id
77-
__span?: string;
78-
};
79-
80-
// TODO Should this be unknown instead? If we vendor types, make it a Response
81-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
82-
response?: any;
83-
error?: unknown;
84-
85-
startTimestamp: number;
86-
endTimestamp?: number;
87-
}
88-
8969
/** Data returned from XHR request */
9070
export interface XHRData {
9171
xhr?: {
@@ -105,17 +85,6 @@ export interface XHRData {
10585
endTimestamp?: number;
10686
}
10787

108-
type PolymorphicRequestHeaders =
109-
| Record<string, string | undefined>
110-
| Array<[string, string]>
111-
// the below is not preicsely the Header type used in Request, but it'll pass duck-typing
112-
| {
113-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
114-
[key: string]: any;
115-
append: (key: string, value: string) => void;
116-
get: (key: string) => string | null | undefined;
117-
};
118-
11988
export const defaultRequestInstrumentationOptions: RequestInstrumentationOptions = {
12089
traceFetch: true,
12190
traceXHR: true,
@@ -154,8 +123,8 @@ export function instrumentOutgoingRequests(_options?: Partial<RequestInstrumenta
154123
const spans: Record<string, Span> = {};
155124

156125
if (traceFetch) {
157-
addInstrumentationHandler('fetch', (handlerData: FetchData) => {
158-
const createdSpan = fetchCallback(handlerData, shouldCreateSpan, shouldAttachHeadersWithTargets, spans);
126+
addInstrumentationHandler('fetch', (handlerData: HandlerDataFetch) => {
127+
const createdSpan = instrumentFetchRequest(handlerData, shouldCreateSpan, shouldAttachHeadersWithTargets, spans);
159128
if (enableHTTPTimings && createdSpan) {
160129
addHTTPTimings(createdSpan);
161130
}
@@ -276,175 +245,6 @@ export function shouldAttachHeaders(url: string, tracePropagationTargets: (strin
276245
return stringMatchesSomePattern(url, tracePropagationTargets || DEFAULT_TRACE_PROPAGATION_TARGETS);
277246
}
278247

279-
/**
280-
* Create and track fetch request spans
281-
*
282-
* @returns Span if a span was created, otherwise void.
283-
*/
284-
export function fetchCallback(
285-
handlerData: FetchData,
286-
shouldCreateSpan: (url: string) => boolean,
287-
shouldAttachHeaders: (url: string) => boolean,
288-
spans: Record<string, Span>,
289-
): Span | undefined {
290-
if (!hasTracingEnabled() || !handlerData.fetchData) {
291-
return undefined;
292-
}
293-
294-
const shouldCreateSpanResult = shouldCreateSpan(handlerData.fetchData.url);
295-
296-
if (handlerData.endTimestamp && shouldCreateSpanResult) {
297-
const spanId = handlerData.fetchData.__span;
298-
if (!spanId) return;
299-
300-
const span = spans[spanId];
301-
if (span) {
302-
if (handlerData.response) {
303-
// TODO (kmclb) remove this once types PR goes through
304-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
305-
span.setHttpStatus(handlerData.response.status);
306-
307-
const contentLength: string =
308-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
309-
handlerData.response && handlerData.response.headers && handlerData.response.headers.get('content-length');
310-
311-
const contentLengthNum = parseInt(contentLength);
312-
if (contentLengthNum > 0) {
313-
span.setData('http.response_content_length', contentLengthNum);
314-
}
315-
} else if (handlerData.error) {
316-
span.setStatus('internal_error');
317-
}
318-
span.finish();
319-
320-
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
321-
delete spans[spanId];
322-
}
323-
return undefined;
324-
}
325-
326-
const hub = getCurrentHub();
327-
const scope = hub.getScope();
328-
const client = hub.getClient();
329-
const parentSpan = scope.getSpan();
330-
331-
const { method, url } = handlerData.fetchData;
332-
333-
const span =
334-
shouldCreateSpanResult && parentSpan
335-
? parentSpan.startChild({
336-
data: {
337-
url,
338-
type: 'fetch',
339-
'http.method': method,
340-
},
341-
description: `${method} ${url}`,
342-
op: 'http.client',
343-
origin: 'auto.http.browser',
344-
})
345-
: undefined;
346-
347-
if (span) {
348-
handlerData.fetchData.__span = span.spanId;
349-
spans[span.spanId] = span;
350-
}
351-
352-
if (shouldAttachHeaders(handlerData.fetchData.url) && client) {
353-
const request: string | Request = handlerData.args[0];
354-
355-
// In case the user hasn't set the second argument of a fetch call we default it to `{}`.
356-
handlerData.args[1] = handlerData.args[1] || {};
357-
358-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
359-
const options: { [key: string]: any } = handlerData.args[1];
360-
361-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
362-
options.headers = addTracingHeadersToFetchRequest(request, client, scope, options, span);
363-
}
364-
365-
return span;
366-
}
367-
368-
/**
369-
* Adds sentry-trace and baggage headers to the various forms of fetch headers
370-
*/
371-
export function addTracingHeadersToFetchRequest(
372-
request: string | unknown, // unknown is actually type Request but we can't export DOM types from this package,
373-
client: Client,
374-
scope: Scope,
375-
options: {
376-
headers?:
377-
| {
378-
[key: string]: string[] | string | undefined;
379-
}
380-
| PolymorphicRequestHeaders;
381-
},
382-
requestSpan?: Span,
383-
): PolymorphicRequestHeaders | undefined {
384-
const span = requestSpan || scope.getSpan();
385-
386-
const transaction = span && span.transaction;
387-
388-
const { traceId, sampled, dsc } = scope.getPropagationContext();
389-
390-
const sentryTraceHeader = span ? span.toTraceparent() : generateSentryTraceHeader(traceId, undefined, sampled);
391-
const dynamicSamplingContext = transaction
392-
? transaction.getDynamicSamplingContext()
393-
: dsc
394-
? dsc
395-
: getDynamicSamplingContextFromClient(traceId, client, scope);
396-
397-
const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext);
398-
399-
const headers =
400-
typeof Request !== 'undefined' && isInstanceOf(request, Request) ? (request as Request).headers : options.headers;
401-
402-
if (!headers) {
403-
return { 'sentry-trace': sentryTraceHeader, baggage: sentryBaggageHeader };
404-
} else if (typeof Headers !== 'undefined' && isInstanceOf(headers, Headers)) {
405-
const newHeaders = new Headers(headers as Headers);
406-
407-
newHeaders.append('sentry-trace', sentryTraceHeader);
408-
409-
if (sentryBaggageHeader) {
410-
// If the same header is appended multiple times the browser will merge the values into a single request header.
411-
// Its therefore safe to simply push a "baggage" entry, even though there might already be another baggage header.
412-
newHeaders.append(BAGGAGE_HEADER_NAME, sentryBaggageHeader);
413-
}
414-
415-
return newHeaders as PolymorphicRequestHeaders;
416-
} else if (Array.isArray(headers)) {
417-
const newHeaders = [...headers, ['sentry-trace', sentryTraceHeader]];
418-
419-
if (sentryBaggageHeader) {
420-
// If there are multiple entries with the same key, the browser will merge the values into a single request header.
421-
// Its therefore safe to simply push a "baggage" entry, even though there might already be another baggage header.
422-
newHeaders.push([BAGGAGE_HEADER_NAME, sentryBaggageHeader]);
423-
}
424-
425-
return newHeaders as PolymorphicRequestHeaders;
426-
} else {
427-
const existingBaggageHeader = 'baggage' in headers ? headers.baggage : undefined;
428-
const newBaggageHeaders: string[] = [];
429-
430-
if (Array.isArray(existingBaggageHeader)) {
431-
newBaggageHeaders.push(...existingBaggageHeader);
432-
} else if (existingBaggageHeader) {
433-
newBaggageHeaders.push(existingBaggageHeader);
434-
}
435-
436-
if (sentryBaggageHeader) {
437-
newBaggageHeaders.push(sentryBaggageHeader);
438-
}
439-
440-
return {
441-
...(headers as Exclude<typeof headers, Headers>),
442-
'sentry-trace': sentryTraceHeader,
443-
baggage: newBaggageHeaders.length > 0 ? newBaggageHeaders.join(',') : undefined,
444-
};
445-
}
446-
}
447-
448248
/**
449249
* Create and track xhr request spans
450250
*

0 commit comments

Comments
 (0)