@@ -10,46 +10,14 @@ import { SENTRY_DYNAMIC_SAMPLING_CONTEXT_KEY, SENTRY_TRACE_PARENT_CONTEXT_KEY }
1010import { isSentryRequestSpan } from './utils/isSentryRequest' ;
1111import { mapOtelStatus } from './utils/mapOtelStatus' ;
1212import { parseSpanDescription } from './utils/parseOtelSpanDescription' ;
13-
14- interface SpanProcessorOptions {
15- /**
16- * By default, if a span is started and we cannot find a Sentry parent span for it,
17- * even if the OTEL span has a parent reference, we will still create the Sentry span as a root span.
18- *
19- * While this is more tolerant of errors, it means that the generated Spans in Sentry may have an incorrect hierarchy.
20- *
21- * When opting into strict span parent handling, we will discard any Spans where we can't find the corresponding parent.
22- * This also requires that we defer clearing of references to the point where the root span is finished -
23- * as sometimes these are not fired in correct order, leading to spans being dropped.
24- *
25- * Note that enabling this is the more correct option
26- * and will probably eventually become the default in a future version.
27- */
28- strictSpanParentHandling : boolean ;
29- }
30-
31- export const SENTRY_SPAN_PROCESSOR_MAP : Map < string , SentrySpan > = new Map < string , SentrySpan > ( ) ;
32-
33- // A map of a sentry span ID to a list of otel span IDs
34- // When the sentry span is finished, clear all references of the given otel spans
35- export const SCHEDULE_TO_CLEAR : Map < string , string [ ] > = new Map < string , string [ ] > ( ) ;
36-
37- /** Get a Sentry span for an otel span ID. */
38- export function getSentrySpan ( otelSpanId : string ) : SentrySpan | undefined {
39- return SENTRY_SPAN_PROCESSOR_MAP . get ( otelSpanId ) ;
40- }
13+ import { clearSpan , getSentrySpan , setSentrySpan } from './utils/spanMap' ;
4114
4215/**
4316 * Converts OpenTelemetry Spans to Sentry Spans and sends them to Sentry via
4417 * the Sentry SDK.
4518 */
4619export class SentrySpanProcessor implements OtelSpanProcessor {
47- private _strictSpanParentHandling : boolean ;
48-
49- public constructor ( { strictSpanParentHandling } : Partial < SpanProcessorOptions > = { } ) {
50- // Default to false
51- this . _strictSpanParentHandling = ! ! strictSpanParentHandling ;
52-
20+ public constructor ( ) {
5321 addTracingExtensions ( ) ;
5422
5523 addGlobalEventProcessor ( event => {
@@ -83,7 +51,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
8351
8452 // Otel supports having multiple non-nested spans at the same time
8553 // so we cannot use hub.getSpan(), as we cannot rely on this being on the current span
86- const sentryParentSpan = otelParentSpanId && SENTRY_SPAN_PROCESSOR_MAP . get ( otelParentSpanId ) ;
54+ const sentryParentSpan = otelParentSpanId && getSentrySpan ( otelParentSpanId ) ;
8755
8856 if ( sentryParentSpan ) {
8957 const sentryChildSpan = sentryParentSpan . startChild ( {
@@ -93,7 +61,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
9361 spanId : otelSpanId ,
9462 } ) ;
9563
96- SENTRY_SPAN_PROCESSOR_MAP . set ( otelSpanId , sentryChildSpan ) ;
64+ setSentrySpan ( otelSpanId , sentryChildSpan ) ;
9765 } else {
9866 const traceCtx = getTraceData ( otelSpan , parentContext ) ;
9967 const transaction = getCurrentHub ( ) . startTransaction ( {
@@ -104,11 +72,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
10472 spanId : otelSpanId ,
10573 } ) ;
10674
107- SENTRY_SPAN_PROCESSOR_MAP . set ( otelSpanId , transaction ) ;
108-
109- if ( this . _strictSpanParentHandling ) {
110- SCHEDULE_TO_CLEAR . set ( transaction . spanId , [ ] ) ;
111- }
75+ setSentrySpan ( otelSpanId , transaction ) ;
11276 }
11377 }
11478
@@ -122,7 +86,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
12286 if ( ! sentrySpan ) {
12387 __DEBUG_BUILD__ &&
12488 logger . error ( `SentrySpanProcessor could not find span with OTEL-spanId ${ otelSpanId } to finish.` ) ;
125- this . _clearSpan ( otelSpanId ) ;
89+ clearSpan ( otelSpanId ) ;
12690 return ;
12791 }
12892
@@ -131,7 +95,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
13195 // leading to an infinite loop.
13296 // In this case, we do not want to finish the span, in order to avoid sending it to Sentry
13397 if ( isSentryRequestSpan ( otelSpan ) ) {
134- this . _clearSpan ( otelSpanId ) ;
98+ clearSpan ( otelSpanId ) ;
13599 return ;
136100 }
137101
@@ -141,7 +105,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
141105 client && client . emit && client ?. emit ( 'otelSpanEnd' , otelSpan , mutableOptions ) ;
142106
143107 if ( mutableOptions . drop ) {
144- this . _clearSpan ( otelSpanId ) ;
108+ clearSpan ( otelSpanId ) ;
145109 return ;
146110 }
147111
@@ -194,7 +158,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
194158
195159 sentrySpan . finish ( convertOtelTimeToSeconds ( otelSpan . endTime ) ) ;
196160
197- this . _clearSpan ( otelSpanId ) ;
161+ clearSpan ( otelSpanId ) ;
198162 }
199163
200164 /**
@@ -214,17 +178,6 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
214178 }
215179 return Promise . resolve ( ) ;
216180 }
217-
218- /**
219- * Clear all references for a given OTEL span.
220- */
221- private _clearSpan ( otelSpanId : string ) : void {
222- if ( this . _strictSpanParentHandling ) {
223- scheduleToClear ( otelSpanId ) ;
224- } else {
225- clearSpan ( otelSpanId ) ;
226- }
227- }
228181}
229182
230183function getTraceData ( otelSpan : OtelSpan , parentContext : Context ) : Partial < TransactionContext > {
@@ -300,50 +253,3 @@ function updateTransactionWithOtelData(transaction: Transaction, otelSpan: OtelS
300253function convertOtelTimeToSeconds ( [ seconds , nano ] : [ number , number ] ) : number {
301254 return seconds + nano / 1_000_000_000 ;
302255}
303-
304- function scheduleToClear ( otelSpanId : string ) : void {
305- const span = SENTRY_SPAN_PROCESSOR_MAP . get ( otelSpanId ) ;
306-
307- if ( ! span ) {
308- // hmm, something is fishy here, but abort...
309- // But to be sure we still try to delete the SCHEDULE_TO_CLEAR, to avoid leaks
310- SCHEDULE_TO_CLEAR . delete ( otelSpanId ) ;
311- return ;
312- }
313-
314- const sentrySpanId = span . spanId ;
315-
316- // This is the root, clear all that have been scheduled
317- if ( spanIsRoot ( span ) || ! span . transaction ) {
318- const toClear = SCHEDULE_TO_CLEAR . get ( sentrySpanId ) || [ ] ;
319- toClear . push ( otelSpanId ) ;
320-
321- toClear . forEach ( otelSpanIdToClear => clearSpan ( otelSpanIdToClear ) ) ;
322- SCHEDULE_TO_CLEAR . delete ( sentrySpanId ) ;
323- return ;
324- }
325-
326- // Clear when root span is cleared
327- const root = span . transaction ;
328- const rootSentrySpanId = root . spanId ;
329-
330- const toClear = SCHEDULE_TO_CLEAR . get ( rootSentrySpanId ) ;
331-
332- // If this does not exist, it means we prob. already cleaned it up before
333- // So we ignore the parent and just clean this span up right now
334- if ( ! toClear ) {
335- clearSpan ( otelSpanId ) ;
336- return ;
337- }
338-
339- toClear . push ( otelSpanId ) ;
340- }
341-
342- function spanIsRoot ( span : SentrySpan ) : span is Transaction {
343- return span . transaction === span ;
344- }
345-
346- // make sure to remove references in maps, to ensure this can be GCed
347- function clearSpan ( otelSpanId : string ) : void {
348- SENTRY_SPAN_PROCESSOR_MAP . delete ( otelSpanId ) ;
349- }
0 commit comments