1
1
import 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 {
4
+ DynamicSamplingContext ,
5
+ EventProcessor ,
6
+ Integration ,
7
+ SanitizedRequestData ,
8
+ TracePropagationTargets ,
9
+ } from '@sentry/types' ;
10
+ import {
11
+ dynamicSamplingContextToSentryBaggageHeader ,
12
+ fill ,
13
+ generateSentryTraceHeader ,
14
+ logger ,
15
+ stringMatchesSomePattern ,
16
+ } from '@sentry/utils' ;
5
17
import type * as http from 'http' ;
6
18
import type * as https from 'https' ;
7
19
import { LRUMap } from 'lru_map' ;
8
20
9
21
import type { NodeClient } from '../client' ;
10
22
import { NODE_VERSION } from '../nodeVersion' ;
11
- import type { RequestMethod , RequestMethodArgs } from './utils/http' ;
23
+ import type { RequestMethod , RequestMethodArgs , RequestOptions } from './utils/http' ;
12
24
import { cleanSpanDescription , extractRawUrl , extractUrl , isSentryRequest , normalizeRequestArgs } from './utils/http' ;
13
25
14
26
interface TracingOptions {
@@ -178,6 +190,36 @@ function _createWrappedRequestMethodFactory(
178
190
return decision ;
179
191
} ;
180
192
193
+ /**
194
+ * Captures Breadcrumb based on provided request/response pair
195
+ */
196
+ function addRequestBreadcrumb (
197
+ event : string ,
198
+ requestSpanData : SanitizedRequestData ,
199
+ req : http . ClientRequest ,
200
+ res ?: http . IncomingMessage ,
201
+ ) : void {
202
+ if ( ! getCurrentHub ( ) . getIntegration ( Http ) ) {
203
+ return ;
204
+ }
205
+
206
+ getCurrentHub ( ) . addBreadcrumb (
207
+ {
208
+ category : 'http' ,
209
+ data : {
210
+ status_code : res && res . statusCode ,
211
+ ...requestSpanData ,
212
+ } ,
213
+ type : 'http' ,
214
+ } ,
215
+ {
216
+ event,
217
+ request : req ,
218
+ response : res ,
219
+ } ,
220
+ ) ;
221
+ }
222
+
181
223
return function wrappedRequestMethodFactory ( originalRequestMethod : OriginalRequestMethod ) : WrappedRequestMethod {
182
224
return function wrappedMethod ( this : unknown , ...args : RequestMethodArgs ) : http . ClientRequest {
183
225
const requestArgs = normalizeRequestArgs ( httpModule , args ) ;
@@ -191,74 +233,38 @@ function _createWrappedRequestMethodFactory(
191
233
return originalRequestMethod . apply ( httpModule , requestArgs ) ;
192
234
}
193
235
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
- }
236
+ const hub = getCurrentHub ( ) ;
237
+ const scope = hub . getScope ( ) ;
238
+ const parentSpan = scope . getSpan ( ) ;
239
+
240
+ const data = getRequestSpanData ( requestUrl , requestOptions ) ;
210
241
211
- if ( tracingOptions && shouldCreateSpan ( rawRequestUrl ) ) {
212
- if ( parentSpan ) {
213
- requestSpan = parentSpan . startChild ( {
214
- description : `${ method } ${ requestSpanData . url } ` ,
242
+ const requestSpan = shouldCreateSpan ( rawRequestUrl )
243
+ ? parentSpan ?. startChild ( {
215
244
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
- ) ;
225
-
226
- requestOptions . headers = {
227
- ...requestOptions . headers ,
228
- 'sentry-trace' : sentryTraceHeader ,
229
- } ;
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
- ) ;
260
- }
245
+ description : `${ data [ 'http.method' ] } ${ data . url } ` ,
246
+ data,
247
+ } )
248
+ : undefined ;
249
+
250
+ if ( shouldAttachTraceData ( rawRequestUrl ) ) {
251
+ if ( requestSpan ) {
252
+ const sentryTraceHeader = requestSpan . toTraceparent ( ) ;
253
+ const dynamicSamplingContext = requestSpan ?. transaction ?. getDynamicSamplingContext ( ) ;
254
+ addHeadersToRequestOptions ( requestOptions , requestUrl , sentryTraceHeader , dynamicSamplingContext ) ;
255
+ } else {
256
+ const client = hub . getClient ( ) ;
257
+ const { traceId, sampled, dsc } = scope . getPropagationContext ( ) ;
258
+ const sentryTraceHeader = generateSentryTraceHeader ( traceId , undefined , sampled ) ;
259
+ const dynamicSamplingContext =
260
+ dsc || ( client ? getDynamicSamplingContextFromClient ( traceId , client , scope ) : undefined ) ;
261
+ addHeadersToRequestOptions ( requestOptions , requestUrl , sentryTraceHeader , dynamicSamplingContext ) ;
261
262
}
263
+ } else {
264
+ __DEBUG_BUILD__ &&
265
+ logger . log (
266
+ `[Tracing] Not adding sentry-trace header to outgoing request (${ requestUrl } ) due to mismatching tracePropagationTargets option.` ,
267
+ ) ;
262
268
}
263
269
264
270
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
@@ -268,7 +274,7 @@ function _createWrappedRequestMethodFactory(
268
274
// eslint-disable-next-line @typescript-eslint/no-this-alias
269
275
const req = this ;
270
276
if ( breadcrumbsEnabled ) {
271
- addRequestBreadcrumb ( 'response' , requestSpanData , req , res ) ;
277
+ addRequestBreadcrumb ( 'response' , data , req , res ) ;
272
278
}
273
279
if ( requestSpan ) {
274
280
if ( res . statusCode ) {
@@ -283,7 +289,7 @@ function _createWrappedRequestMethodFactory(
283
289
const req = this ;
284
290
285
291
if ( breadcrumbsEnabled ) {
286
- addRequestBreadcrumb ( 'error' , requestSpanData , req ) ;
292
+ addRequestBreadcrumb ( 'error' , data , req ) ;
287
293
}
288
294
if ( requestSpan ) {
289
295
requestSpan . setHttpStatus ( 500 ) ;
@@ -295,32 +301,55 @@ function _createWrappedRequestMethodFactory(
295
301
} ;
296
302
}
297
303
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 ,
304
+ function addHeadersToRequestOptions (
305
+ requestOptions : RequestOptions ,
306
+ requestUrl : string ,
307
+ sentryTraceHeader : string ,
308
+ dynamicSamplingContext : Partial < DynamicSamplingContext > | undefined ,
306
309
) : void {
307
- if ( ! getCurrentHub ( ) . getIntegration ( Http ) ) {
308
- return ;
310
+ __DEBUG_BUILD__ &&
311
+ logger . log ( `[Tracing] Adding sentry-trace header ${ sentryTraceHeader } to outgoing request to "${ requestUrl } ": ` ) ;
312
+ const sentryBaggage = dynamicSamplingContextToSentryBaggageHeader ( dynamicSamplingContext ) ;
313
+ const sentryBaggageHeader = normalizeBaggageHeader ( requestOptions , sentryBaggage ) ;
314
+ requestOptions . headers = {
315
+ ...requestOptions . headers ,
316
+ 'sentry-trace' : sentryTraceHeader ,
317
+ // Setting a header to `undefined` will crash in node so we only set the baggage header when it's defined
318
+ ...( sentryBaggageHeader && { baggage : sentryBaggageHeader } ) ,
319
+ } ;
320
+ }
321
+
322
+ function getRequestSpanData ( requestUrl : string , requestOptions : RequestOptions ) : SanitizedRequestData {
323
+ const method = requestOptions . method || 'GET' ;
324
+ const data : SanitizedRequestData = {
325
+ url : requestUrl ,
326
+ 'http.method' : method ,
327
+ } ;
328
+ if ( requestOptions . hash ) {
329
+ // strip leading "#"
330
+ data [ 'http.fragment' ] = requestOptions . hash . substring ( 1 ) ;
331
+ }
332
+ if ( requestOptions . search ) {
333
+ // strip leading "?"
334
+ data [ 'http.query' ] = requestOptions . search . substring ( 1 ) ;
335
+ }
336
+ return data ;
337
+ }
338
+
339
+ function normalizeBaggageHeader (
340
+ requestOptions : RequestOptions ,
341
+ sentryBaggageHeader : string | undefined ,
342
+ ) : string | string [ ] | undefined {
343
+ if ( ! requestOptions . headers || ! requestOptions . headers . baggage ) {
344
+ return sentryBaggageHeader ;
345
+ } else if ( ! sentryBaggageHeader ) {
346
+ return requestOptions . headers . baggage as string | string [ ] ;
347
+ } else if ( Array . isArray ( requestOptions . headers . baggage ) ) {
348
+ return [ ...requestOptions . headers . baggage , sentryBaggageHeader ] ;
309
349
}
310
350
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
- ) ;
351
+ // Type-cast explanation:
352
+ // Technically this the following could be of type `(number | string)[]` but for the sake of simplicity
353
+ // we say this is undefined behaviour, since it would not be baggage spec conform if the user did this.
354
+ return [ requestOptions . headers . baggage , sentryBaggageHeader ] as string [ ] ;
326
355
}
0 commit comments