11import type { Hub } from '@sentry/core' ;
2- import { getCurrentHub } from '@sentry/core' ;
3- import type { EventProcessor , Integration , SanitizedRequestData , Span , TracePropagationTargets } from '@sentry/types' ;
4- import { dynamicSamplingContextToSentryBaggageHeader , fill , logger , stringMatchesSomePattern } from '@sentry/utils' ;
2+ import { getCurrentHub , getDynamicSamplingContextFromClient } from '@sentry/core' ;
3+ import type { EventProcessor , Integration , SanitizedRequestData , TracePropagationTargets } from '@sentry/types' ;
4+ import {
5+ dynamicSamplingContextToSentryBaggageHeader ,
6+ fill ,
7+ generateSentryTraceHeader ,
8+ logger ,
9+ stringMatchesSomePattern ,
10+ } from '@sentry/utils' ;
511import type * as http from 'http' ;
612import type * as https from 'https' ;
713import { LRUMap } from 'lru_map' ;
814
915import type { NodeClient } from '../client' ;
1016import { NODE_VERSION } from '../nodeVersion' ;
11- import type { RequestMethod , RequestMethodArgs } from './utils/http' ;
17+ import type { RequestMethod , RequestMethodArgs , RequestOptions } from './utils/http' ;
1218import { cleanSpanDescription , extractRawUrl , extractUrl , isSentryRequest , normalizeRequestArgs } from './utils/http' ;
1319
1420interface TracingOptions {
@@ -178,6 +184,36 @@ function _createWrappedRequestMethodFactory(
178184 return decision ;
179185 } ;
180186
187+ /**
188+ * Captures Breadcrumb based on provided request/response pair
189+ */
190+ function addRequestBreadcrumb (
191+ event : string ,
192+ requestSpanData : SanitizedRequestData ,
193+ req : http . ClientRequest ,
194+ res ?: http . IncomingMessage ,
195+ ) : void {
196+ if ( ! getCurrentHub ( ) . getIntegration ( Http ) ) {
197+ return ;
198+ }
199+
200+ getCurrentHub ( ) . addBreadcrumb (
201+ {
202+ category : 'http' ,
203+ data : {
204+ status_code : res && res . statusCode ,
205+ ...requestSpanData ,
206+ } ,
207+ type : 'http' ,
208+ } ,
209+ {
210+ event,
211+ request : req ,
212+ response : res ,
213+ } ,
214+ ) ;
215+ }
216+
181217 return function wrappedRequestMethodFactory ( originalRequestMethod : OriginalRequestMethod ) : WrappedRequestMethod {
182218 return function wrappedMethod ( this : unknown , ...args : RequestMethodArgs ) : http . ClientRequest {
183219 const requestArgs = normalizeRequestArgs ( httpModule , args ) ;
@@ -191,74 +227,66 @@ function _createWrappedRequestMethodFactory(
191227 return originalRequestMethod . apply ( httpModule , requestArgs ) ;
192228 }
193229
194- let requestSpan : Span | undefined ;
195- const parentSpan = getCurrentHub ( ) . getScope ( ) . getSpan ( ) ;
196-
197- const method = requestOptions . method || 'GET' ;
198- const requestSpanData : SanitizedRequestData = {
199- url : requestUrl ,
200- 'http.method' : method ,
201- } ;
202- if ( requestOptions . hash ) {
203- // strip leading "#"
204- requestSpanData [ 'http.fragment' ] = requestOptions . hash . substring ( 1 ) ;
205- }
206- if ( requestOptions . search ) {
207- // strip leading "?"
208- requestSpanData [ 'http.query' ] = requestOptions . search . substring ( 1 ) ;
209- }
230+ const hub = getCurrentHub ( ) ;
231+ const scope = hub . getScope ( ) ;
232+ const parentSpan = scope . getSpan ( ) ;
210233
211- if ( tracingOptions && shouldCreateSpan ( rawRequestUrl ) ) {
212- if ( parentSpan ) {
213- requestSpan = parentSpan . startChild ( {
214- description : `${ method } ${ requestSpanData . url } ` ,
215- op : 'http.client' ,
216- data : requestSpanData ,
217- } ) ;
218-
219- if ( shouldAttachTraceData ( rawRequestUrl ) ) {
220- const sentryTraceHeader = requestSpan . toTraceparent ( ) ;
221- __DEBUG_BUILD__ &&
222- logger . log (
223- `[Tracing] Adding sentry-trace header ${ sentryTraceHeader } to outgoing request to "${ requestUrl } ": ` ,
224- ) ;
234+ const data = getRequestSpanData ( requestUrl , requestOptions ) ;
225235
236+ const requestSpan = shouldCreateSpan ( rawRequestUrl )
237+ ? parentSpan ?. startChild ( {
238+ op : 'http.client' ,
239+ description : `${ data [ 'http.method' ] } ${ data . url } ` ,
240+ data,
241+ } )
242+ : undefined ;
243+
244+ if ( shouldAttachTraceData ( rawRequestUrl ) ) {
245+ if ( requestSpan ) {
246+ const sentryTraceHeader = requestSpan . toTraceparent ( ) ;
247+ __DEBUG_BUILD__ &&
248+ logger . log (
249+ `[Tracing] Adding sentry-trace header ${ sentryTraceHeader } to outgoing request to "${ requestUrl } ": ` ,
250+ ) ;
251+ const dynamicSamplingContext = requestSpan ?. transaction ?. getDynamicSamplingContext ( ) ;
252+ const sentryBaggageHeader = normalizeBaggageHeader (
253+ requestOptions ,
254+ dynamicSamplingContextToSentryBaggageHeader ( dynamicSamplingContext ) ,
255+ ) ;
256+
257+ requestOptions . headers = {
258+ ...requestOptions . headers ,
259+ 'sentry-trace' : sentryTraceHeader ,
260+ // Setting a header to `undefined` will crash in node so we only set the baggage header when it's defined
261+ ...( sentryBaggageHeader && { baggage : sentryBaggageHeader } ) ,
262+ } ;
263+ } else {
264+ const { traceId, sampled, dsc } = scope . getPropagationContext ( ) ;
265+ const sentryTraceHeader = generateSentryTraceHeader ( traceId , undefined , sampled ) ;
266+ __DEBUG_BUILD__ &&
267+ logger . log (
268+ `[Tracing] Adding sentry-trace header ${ sentryTraceHeader } to outgoing request to "${ requestUrl } ": ` ,
269+ ) ;
270+ requestOptions . headers = {
271+ ...requestOptions . headers ,
272+ 'sentry-trace' : sentryTraceHeader ,
273+ } ;
274+ const client = hub . getClient ( ) ;
275+ if ( client ) {
276+ const dynamicSamplingContext = dsc || getDynamicSamplingContextFromClient ( traceId , client , scope ) ;
277+ const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader ( dynamicSamplingContext ) ;
226278 requestOptions . headers = {
227279 ...requestOptions . headers ,
228- 'sentry-trace' : sentryTraceHeader ,
280+ // Setting a header to `undefined` will crash in node so we only set the baggage header when it's defined
281+ ...( sentryBaggageHeader && { baggage : sentryBaggageHeader } ) ,
229282 } ;
230-
231- if ( parentSpan . transaction ) {
232- const dynamicSamplingContext = parentSpan . transaction . getDynamicSamplingContext ( ) ;
233- const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader ( dynamicSamplingContext ) ;
234-
235- let newBaggageHeaderField ;
236- if ( ! requestOptions . headers || ! requestOptions . headers . baggage ) {
237- newBaggageHeaderField = sentryBaggageHeader ;
238- } else if ( ! sentryBaggageHeader ) {
239- newBaggageHeaderField = requestOptions . headers . baggage ;
240- } else if ( Array . isArray ( requestOptions . headers . baggage ) ) {
241- newBaggageHeaderField = [ ...requestOptions . headers . baggage , sentryBaggageHeader ] ;
242- } else {
243- // Type-cast explanation:
244- // Technically this the following could be of type `(number | string)[]` but for the sake of simplicity
245- // we say this is undefined behaviour, since it would not be baggage spec conform if the user did this.
246- newBaggageHeaderField = [ requestOptions . headers . baggage , sentryBaggageHeader ] as string [ ] ;
247- }
248-
249- requestOptions . headers = {
250- ...requestOptions . headers ,
251- // Setting a hader to `undefined` will crash in node so we only set the baggage header when it's defined
252- ...( newBaggageHeaderField && { baggage : newBaggageHeaderField } ) ,
253- } ;
254- }
255- } else {
256- __DEBUG_BUILD__ &&
257- logger . log (
258- `[Tracing] Not adding sentry-trace header to outgoing request (${ requestUrl } ) due to mismatching tracePropagationTargets option.` ,
259- ) ;
260283 }
261284 }
285+ } else {
286+ __DEBUG_BUILD__ &&
287+ logger . log (
288+ `[Tracing] Not adding sentry-trace header to outgoing request (${ requestUrl } ) due to mismatching tracePropagationTargets option.` ,
289+ ) ;
262290 }
263291
264292 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
@@ -268,7 +296,7 @@ function _createWrappedRequestMethodFactory(
268296 // eslint-disable-next-line @typescript-eslint/no-this-alias
269297 const req = this ;
270298 if ( breadcrumbsEnabled ) {
271- addRequestBreadcrumb ( 'response' , requestSpanData , req , res ) ;
299+ addRequestBreadcrumb ( 'response' , data , req , res ) ;
272300 }
273301 if ( requestSpan ) {
274302 if ( res . statusCode ) {
@@ -283,7 +311,7 @@ function _createWrappedRequestMethodFactory(
283311 const req = this ;
284312
285313 if ( breadcrumbsEnabled ) {
286- addRequestBreadcrumb ( 'error' , requestSpanData , req ) ;
314+ addRequestBreadcrumb ( 'error' , data , req ) ;
287315 }
288316 if ( requestSpan ) {
289317 requestSpan . setHttpStatus ( 500 ) ;
@@ -295,32 +323,37 @@ function _createWrappedRequestMethodFactory(
295323 } ;
296324}
297325
298- /**
299- * Captures Breadcrumb based on provided request/response pair
300- */
301- function addRequestBreadcrumb (
302- event : string ,
303- requestSpanData : SanitizedRequestData ,
304- req : http . ClientRequest ,
305- res ?: http . IncomingMessage ,
306- ) : void {
307- if ( ! getCurrentHub ( ) . getIntegration ( Http ) ) {
308- return ;
326+ function getRequestSpanData ( requestUrl : string , requestOptions : RequestOptions ) : SanitizedRequestData {
327+ const method = requestOptions . method || 'GET' ;
328+ const data : SanitizedRequestData = {
329+ url : requestUrl ,
330+ 'http.method' : method ,
331+ } ;
332+ if ( requestOptions . hash ) {
333+ // strip leading "#"
334+ data [ 'http.fragment' ] = requestOptions . hash . substring ( 1 ) ;
335+ }
336+ if ( requestOptions . search ) {
337+ // strip leading "?"
338+ data [ 'http.query' ] = requestOptions . search . substring ( 1 ) ;
339+ }
340+ return data ;
341+ }
342+
343+ function normalizeBaggageHeader (
344+ requestOptions : RequestOptions ,
345+ sentryBaggageHeader : string | undefined ,
346+ ) : string | number | string [ ] | undefined {
347+ if ( ! requestOptions . headers || ! requestOptions . headers . baggage ) {
348+ return sentryBaggageHeader ;
349+ } else if ( ! sentryBaggageHeader ) {
350+ return requestOptions . headers . baggage ;
351+ } else if ( Array . isArray ( requestOptions . headers . baggage ) ) {
352+ return [ ...requestOptions . headers . baggage , sentryBaggageHeader ] ;
309353 }
310354
311- getCurrentHub ( ) . addBreadcrumb (
312- {
313- category : 'http' ,
314- data : {
315- status_code : res && res . statusCode ,
316- ...requestSpanData ,
317- } ,
318- type : 'http' ,
319- } ,
320- {
321- event,
322- request : req ,
323- response : res ,
324- } ,
325- ) ;
355+ // Type-cast explanation:
356+ // Technically this the following could be of type `(number | string)[]` but for the sake of simplicity
357+ // we say this is undefined behaviour, since it would not be baggage spec conform if the user did this.
358+ return [ requestOptions . headers . baggage , sentryBaggageHeader ] as string [ ] ;
326359}
0 commit comments