11import type { Hub } from '@sentry/core' ;
2- import type { EventProcessor , Integration } from '@sentry/types' ;
2+ import { getDynamicSamplingContextFromClient } from '@sentry/core' ;
3+ import type { EventProcessor , Integration , Span } from '@sentry/types' ;
34import {
45 dynamicRequire ,
56 dynamicSamplingContextToSentryBaggageHeader ,
7+ generateSentryTraceHeader ,
68 getSanitizedUrlString ,
79 parseUrl ,
810 stringMatchesSomePattern ,
@@ -12,7 +14,13 @@ import { LRUMap } from 'lru_map';
1214import type { NodeClient } from '../../client' ;
1315import { NODE_VERSION } from '../../nodeVersion' ;
1416import { isSentryRequest } from '../utils/http' ;
15- import type { DiagnosticsChannel , RequestCreateMessage , RequestEndMessage , RequestErrorMessage } from './types' ;
17+ import type {
18+ DiagnosticsChannel ,
19+ RequestCreateMessage ,
20+ RequestEndMessage ,
21+ RequestErrorMessage ,
22+ RequestWithSentry ,
23+ } from './types' ;
1624
1725export enum ChannelName {
1826 // https://github.com/nodejs/undici/blob/e6fc80f809d1217814c044f52ed40ef13f21e43c/docs/api/DiagnosticsChannel.md#undicirequestcreate
@@ -124,63 +132,53 @@ export class Undici implements Integration {
124132 const { request } = message as RequestCreateMessage ;
125133
126134 const stringUrl = request . origin ? request . origin . toString ( ) + request . path : request . path ;
127- const url = parseUrl ( stringUrl ) ;
128135
129- if ( isSentryRequest ( stringUrl ) || request . __sentry__ !== undefined ) {
136+ if ( isSentryRequest ( stringUrl ) || request . __sentry_span__ !== undefined ) {
130137 return ;
131138 }
132139
133140 const client = hub . getClient < NodeClient > ( ) ;
141+ if ( ! client ) {
142+ return ;
143+ }
144+
145+ const clientOptions = client . getOptions ( ) ;
134146 const scope = hub . getScope ( ) ;
135147
136- const activeSpan = scope . getSpan ( ) ;
137-
138- if ( activeSpan && client ) {
139- const clientOptions = client . getOptions ( ) ;
140-
141- if ( shouldCreateSpan ( stringUrl ) ) {
142- const method = request . method || 'GET' ;
143- const data : Record < string , unknown > = {
144- 'http.method' : method ,
145- } ;
146- if ( url . search ) {
147- data [ 'http.query' ] = url . search ;
148- }
149- if ( url . hash ) {
150- data [ 'http.fragment' ] = url . hash ;
151- }
152- const span = activeSpan . startChild ( {
153- op : 'http.client' ,
154- description : `${ method } ${ getSanitizedUrlString ( url ) } ` ,
155- data,
156- } ) ;
157- request . __sentry__ = span ;
158-
159- const shouldAttachTraceData = ( url : string ) : boolean => {
160- if ( clientOptions . tracePropagationTargets === undefined ) {
161- return true ;
162- }
163-
164- const cachedDecision = this . _headersUrlMap . get ( url ) ;
165- if ( cachedDecision !== undefined ) {
166- return cachedDecision ;
167- }
168-
169- const decision = stringMatchesSomePattern ( url , clientOptions . tracePropagationTargets ) ;
170- this . _headersUrlMap . set ( url , decision ) ;
171- return decision ;
172- } ;
173-
174- if ( shouldAttachTraceData ( stringUrl ) ) {
175- request . addHeader ( 'sentry-trace' , span . toTraceparent ( ) ) ;
176- if ( span . transaction ) {
177- const dynamicSamplingContext = span . transaction . getDynamicSamplingContext ( ) ;
178- const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader ( dynamicSamplingContext ) ;
179- if ( sentryBaggageHeader ) {
180- request . addHeader ( 'baggage' , sentryBaggageHeader ) ;
181- }
182- }
183- }
148+ const parentSpan = scope . getSpan ( ) ;
149+
150+ const span = shouldCreateSpan ( stringUrl ) ? createRequestSpan ( parentSpan , request , stringUrl ) : undefined ;
151+ if ( span ) {
152+ request . __sentry_span__ = span ;
153+ }
154+
155+ const shouldAttachTraceData = ( url : string ) : boolean => {
156+ if ( clientOptions . tracePropagationTargets === undefined ) {
157+ return true ;
158+ }
159+
160+ const cachedDecision = this . _headersUrlMap . get ( url ) ;
161+ if ( cachedDecision !== undefined ) {
162+ return cachedDecision ;
163+ }
164+
165+ const decision = stringMatchesSomePattern ( url , clientOptions . tracePropagationTargets ) ;
166+ this . _headersUrlMap . set ( url , decision ) ;
167+ return decision ;
168+ } ;
169+
170+ if ( shouldAttachTraceData ( stringUrl ) ) {
171+ if ( span ) {
172+ const dynamicSamplingContext = span ?. transaction ?. getDynamicSamplingContext ( ) ;
173+ const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader ( dynamicSamplingContext ) ;
174+
175+ setHeadersOnRequest ( request , span . toTraceparent ( ) , sentryBaggageHeader ) ;
176+ } else {
177+ const { traceId, sampled, dsc } = scope . getPropagationContext ( ) ;
178+ const sentryTrace = generateSentryTraceHeader ( traceId , undefined , sampled ) ;
179+ const dynamicSamplingContext = dsc || getDynamicSamplingContextFromClient ( traceId , client , scope ) ;
180+ const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader ( dynamicSamplingContext ) ;
181+ setHeadersOnRequest ( request , sentryTrace , sentryBaggageHeader ) ;
184182 }
185183 }
186184 } ) ;
@@ -199,7 +197,7 @@ export class Undici implements Integration {
199197 return ;
200198 }
201199
202- const span = request . __sentry__ ;
200+ const span = request . __sentry_span__ ;
203201 if ( span ) {
204202 span . setHttpStatus ( response . statusCode ) ;
205203 span . finish ( ) ;
@@ -239,7 +237,7 @@ export class Undici implements Integration {
239237 return ;
240238 }
241239
242- const span = request . __sentry__ ;
240+ const span = request . __sentry_span__ ;
243241 if ( span ) {
244242 span . setStatus ( 'internal_error' ) ;
245243 span . finish ( ) ;
@@ -265,3 +263,44 @@ export class Undici implements Integration {
265263 } ) ;
266264 }
267265}
266+
267+ function setHeadersOnRequest (
268+ request : RequestWithSentry ,
269+ sentryTrace : string ,
270+ sentryBaggageHeader : string | undefined ,
271+ ) : void {
272+ if ( request . __sentry_has_headers__ ) {
273+ return ;
274+ }
275+
276+ request . addHeader ( 'sentry-trace' , sentryTrace ) ;
277+ if ( sentryBaggageHeader ) {
278+ request . addHeader ( 'baggage' , sentryBaggageHeader ) ;
279+ }
280+
281+ request . __sentry_has_headers__ = true ;
282+ }
283+
284+ function createRequestSpan (
285+ activeSpan : Span | undefined ,
286+ request : RequestWithSentry ,
287+ stringUrl : string ,
288+ ) : Span | undefined {
289+ const url = parseUrl ( stringUrl ) ;
290+
291+ const method = request . method || 'GET' ;
292+ const data : Record < string , unknown > = {
293+ 'http.method' : method ,
294+ } ;
295+ if ( url . search ) {
296+ data [ 'http.query' ] = url . search ;
297+ }
298+ if ( url . hash ) {
299+ data [ 'http.fragment' ] = url . hash ;
300+ }
301+ return activeSpan ?. startChild ( {
302+ op : 'http.client' ,
303+ description : `${ method } ${ getSanitizedUrlString ( url ) } ` ,
304+ data,
305+ } ) ;
306+ }
0 commit comments