@@ -2,29 +2,16 @@ import { context } from '@opentelemetry/api';
22import { isTracingSuppressed } from '@opentelemetry/core' ;
33import type { InstrumentationConfig } from '@opentelemetry/instrumentation' ;
44import { InstrumentationBase } from '@opentelemetry/instrumentation' ;
5- import type { SanitizedRequestData } from '@sentry/core' ;
6- import {
7- addBreadcrumb ,
8- getBreadcrumbLogLevelFromHttpStatusCode ,
9- getClient ,
10- getSanitizedUrlString ,
11- getTraceData ,
12- LRUMap ,
13- parseUrl ,
14- SDK_VERSION ,
15- } from '@sentry/core' ;
16- import { shouldPropagateTraceForUrl } from '@sentry/opentelemetry' ;
5+ import { LRUMap , SDK_VERSION } from '@sentry/core' ;
176import * as diagch from 'diagnostics_channel' ;
187import { NODE_MAJOR , NODE_MINOR } from '../../nodeVersion' ;
19- import { mergeBaggageHeaders } from '../../utils/baggage' ;
8+ import {
9+ addFetchRequestBreadcrumb ,
10+ addTracePropagationHeadersToFetchRequest ,
11+ getAbsoluteUrl ,
12+ } from '../../utils/outgoingFetchRequest' ;
2013import type { UndiciRequest , UndiciResponse } from './types' ;
2114
22- const SENTRY_TRACE_HEADER = 'sentry-trace' ;
23- const SENTRY_BAGGAGE_HEADER = 'baggage' ;
24-
25- // For baggage, we make sure to merge this into a possibly existing header
26- const BAGGAGE_HEADER_REGEX = / b a g g a g e : ( .* ) \r \n / ;
27-
2815export type SentryNodeFetchInstrumentationOptions = InstrumentationConfig & {
2916 /**
3017 * Whether breadcrumbs should be recorded for requests.
@@ -114,7 +101,6 @@ export class SentryNodeFetchInstrumentation extends InstrumentationBase<SentryNo
114101 * This method is called when a request is created.
115102 * You can still mutate the request here before it is sent.
116103 */
117- // eslint-disable-next-line complexity
118104 private _onRequestCreated ( { request } : { request : UndiciRequest } ) : void {
119105 const config = this . getConfig ( ) ;
120106 const enabled = config . enabled !== false ;
@@ -132,70 +118,7 @@ export class SentryNodeFetchInstrumentation extends InstrumentationBase<SentryNo
132118 return ;
133119 }
134120
135- const url = getAbsoluteUrl ( request . origin , request . path ) ;
136-
137- // Manually add the trace headers, if it applies
138- // Note: We do not use `propagation.inject()` here, because our propagator relies on an active span
139- // Which we do not have in this case
140- // The propagator _may_ overwrite this, but this should be fine as it is the same data
141- const { tracePropagationTargets, propagateTraceparent } = getClient ( ) ?. getOptions ( ) || { } ;
142- const addedHeaders = shouldPropagateTraceForUrl ( url , tracePropagationTargets , this . _propagationDecisionMap )
143- ? getTraceData ( { propagateTraceparent } )
144- : undefined ;
145-
146- if ( ! addedHeaders ) {
147- return ;
148- }
149-
150- const { 'sentry-trace' : sentryTrace , baggage, traceparent } = addedHeaders ;
151-
152- // We do not want to overwrite existing headers here
153- // If the core UndiciInstrumentation is registered, it will already have set the headers
154- // We do not want to add any then
155- if ( Array . isArray ( request . headers ) ) {
156- const requestHeaders = request . headers ;
157-
158- // We do not want to overwrite existing header here, if it was already set
159- if ( sentryTrace && ! requestHeaders . includes ( SENTRY_TRACE_HEADER ) ) {
160- requestHeaders . push ( SENTRY_TRACE_HEADER , sentryTrace ) ;
161- }
162-
163- if ( traceparent && ! requestHeaders . includes ( 'traceparent' ) ) {
164- requestHeaders . push ( 'traceparent' , traceparent ) ;
165- }
166-
167- // For baggage, we make sure to merge this into a possibly existing header
168- const existingBaggagePos = requestHeaders . findIndex ( header => header === SENTRY_BAGGAGE_HEADER ) ;
169- if ( baggage && existingBaggagePos === - 1 ) {
170- requestHeaders . push ( SENTRY_BAGGAGE_HEADER , baggage ) ;
171- } else if ( baggage ) {
172- const existingBaggage = requestHeaders [ existingBaggagePos + 1 ] ;
173- const merged = mergeBaggageHeaders ( existingBaggage , baggage ) ;
174- if ( merged ) {
175- requestHeaders [ existingBaggagePos + 1 ] = merged ;
176- }
177- }
178- } else {
179- const requestHeaders = request . headers ;
180- // We do not want to overwrite existing header here, if it was already set
181- if ( sentryTrace && ! requestHeaders . includes ( `${ SENTRY_TRACE_HEADER } :` ) ) {
182- request . headers += `${ SENTRY_TRACE_HEADER } : ${ sentryTrace } \r\n` ;
183- }
184-
185- if ( traceparent && ! requestHeaders . includes ( 'traceparent:' ) ) {
186- request . headers += `traceparent: ${ traceparent } \r\n` ;
187- }
188-
189- const existingBaggage = request . headers . match ( BAGGAGE_HEADER_REGEX ) ?. [ 1 ] ;
190- if ( baggage && ! existingBaggage ) {
191- request . headers += `${ SENTRY_BAGGAGE_HEADER } : ${ baggage } \r\n` ;
192- } else if ( baggage ) {
193- const merged = mergeBaggageHeaders ( existingBaggage , baggage ) ;
194- if ( merged ) {
195- request . headers = request . headers . replace ( BAGGAGE_HEADER_REGEX , `baggage: ${ merged } \r\n` ) ;
196- }
197- }
198- }
121+ addTracePropagationHeadersToFetchRequest ( request , this . _propagationDecisionMap ) ;
199122 }
200123
201124 /**
@@ -215,7 +138,7 @@ export class SentryNodeFetchInstrumentation extends InstrumentationBase<SentryNo
215138 const shouldIgnore = this . _ignoreOutgoingRequestsMap . get ( request ) ;
216139
217140 if ( breadCrumbsEnabled && ! shouldIgnore ) {
218- addRequestBreadcrumb ( request , response ) ;
141+ addFetchRequestBreadcrumb ( request , response ) ;
219142 }
220143 }
221144
@@ -263,71 +186,3 @@ export class SentryNodeFetchInstrumentation extends InstrumentationBase<SentryNo
263186 return ignoreOutgoingRequests ( url ) ;
264187 }
265188}
266-
267- /** Add a breadcrumb for outgoing requests. */
268- function addRequestBreadcrumb ( request : UndiciRequest , response : UndiciResponse ) : void {
269- const data = getBreadcrumbData ( request ) ;
270-
271- const statusCode = response . statusCode ;
272- const level = getBreadcrumbLogLevelFromHttpStatusCode ( statusCode ) ;
273-
274- addBreadcrumb (
275- {
276- category : 'http' ,
277- data : {
278- status_code : statusCode ,
279- ...data ,
280- } ,
281- type : 'http' ,
282- level,
283- } ,
284- {
285- event : 'response' ,
286- request,
287- response,
288- } ,
289- ) ;
290- }
291-
292- function getBreadcrumbData ( request : UndiciRequest ) : Partial < SanitizedRequestData > {
293- try {
294- const url = getAbsoluteUrl ( request . origin , request . path ) ;
295- const parsedUrl = parseUrl ( url ) ;
296-
297- const data : Partial < SanitizedRequestData > = {
298- url : getSanitizedUrlString ( parsedUrl ) ,
299- 'http.method' : request . method || 'GET' ,
300- } ;
301-
302- if ( parsedUrl . search ) {
303- data [ 'http.query' ] = parsedUrl . search ;
304- }
305- if ( parsedUrl . hash ) {
306- data [ 'http.fragment' ] = parsedUrl . hash ;
307- }
308-
309- return data ;
310- } catch {
311- return { } ;
312- }
313- }
314-
315- function getAbsoluteUrl ( origin : string , path : string = '/' ) : string {
316- try {
317- const url = new URL ( path , origin ) ;
318- return url . toString ( ) ;
319- } catch {
320- // fallback: Construct it on our own
321- const url = `${ origin } ` ;
322-
323- if ( url . endsWith ( '/' ) && path . startsWith ( '/' ) ) {
324- return `${ url } ${ path . slice ( 1 ) } ` ;
325- }
326-
327- if ( ! url . endsWith ( '/' ) && ! path . startsWith ( '/' ) ) {
328- return `${ url } /${ path . slice ( 1 ) } ` ;
329- }
330-
331- return `${ url } ${ path } ` ;
332- }
333- }
0 commit comments