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 { EventProcessor , Integration , SanitizedRequestData , TracePropagationTargets } from '@sentry/types' ;
4
+ import {
5
+ dynamicSamplingContextToSentryBaggageHeader ,
6
+ fill ,
7
+ generateSentryTraceHeader ,
8
+ logger ,
9
+ stringMatchesSomePattern ,
10
+ } from '@sentry/utils' ;
5
11
import type * as http from 'http' ;
6
12
import type * as https from 'https' ;
7
13
import { LRUMap } from 'lru_map' ;
8
14
9
15
import type { NodeClient } from '../client' ;
10
16
import { NODE_VERSION } from '../nodeVersion' ;
11
- import type { RequestMethod , RequestMethodArgs } from './utils/http' ;
17
+ import type { RequestMethod , RequestMethodArgs , RequestOptions } from './utils/http' ;
12
18
import { cleanSpanDescription , extractRawUrl , extractUrl , isSentryRequest , normalizeRequestArgs } from './utils/http' ;
13
19
14
20
interface TracingOptions {
@@ -178,6 +184,36 @@ function _createWrappedRequestMethodFactory(
178
184
return decision ;
179
185
} ;
180
186
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
+
181
217
return function wrappedRequestMethodFactory ( originalRequestMethod : OriginalRequestMethod ) : WrappedRequestMethod {
182
218
return function wrappedMethod ( this : unknown , ...args : RequestMethodArgs ) : http . ClientRequest {
183
219
const requestArgs = normalizeRequestArgs ( httpModule , args ) ;
@@ -191,74 +227,66 @@ function _createWrappedRequestMethodFactory(
191
227
return originalRequestMethod . apply ( httpModule , requestArgs ) ;
192
228
}
193
229
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 ( ) ;
210
233
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 ) ;
225
235
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 ) ;
226
278
requestOptions . headers = {
227
279
...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 } ) ,
229
282
} ;
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
283
}
261
284
}
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
+ ) ;
262
290
}
263
291
264
292
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
@@ -268,7 +296,7 @@ function _createWrappedRequestMethodFactory(
268
296
// eslint-disable-next-line @typescript-eslint/no-this-alias
269
297
const req = this ;
270
298
if ( breadcrumbsEnabled ) {
271
- addRequestBreadcrumb ( 'response' , requestSpanData , req , res ) ;
299
+ addRequestBreadcrumb ( 'response' , data , req , res ) ;
272
300
}
273
301
if ( requestSpan ) {
274
302
if ( res . statusCode ) {
@@ -283,7 +311,7 @@ function _createWrappedRequestMethodFactory(
283
311
const req = this ;
284
312
285
313
if ( breadcrumbsEnabled ) {
286
- addRequestBreadcrumb ( 'error' , requestSpanData , req ) ;
314
+ addRequestBreadcrumb ( 'error' , data , req ) ;
287
315
}
288
316
if ( requestSpan ) {
289
317
requestSpan . setHttpStatus ( 500 ) ;
@@ -295,32 +323,37 @@ function _createWrappedRequestMethodFactory(
295
323
} ;
296
324
}
297
325
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 ] ;
309
353
}
310
354
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 [ ] ;
326
359
}
0 commit comments