@@ -6,8 +6,10 @@ import {
66 defineIntegration ,
77 getDynamicSamplingContextFromSpan ,
88 isV2BeforeSendSpanCallback ,
9+ SpanBuffer ,
910} from '@sentry/core' ;
1011import { DEBUG_BUILD } from '../debug-build' ;
12+ import { WINDOW } from '../helpers' ;
1113
1214export interface SpanStreamingOptions {
1315 batchLimit : number ;
@@ -31,12 +33,10 @@ export const spanStreamingIntegration = defineIntegration(((userOptions?: Partia
3133 : 1000 ,
3234 } ;
3335
34- // key: traceId-segmentSpanId
35- const spanTreeMap = new Map < string , Set < SpanV2JSONWithSegmentRef > > ( ) ;
36-
3736 return {
3837 name : 'SpanStreaming' ,
3938 setup ( client ) {
39+ const buffer = new SpanBuffer ( client ) ;
4040 const clientOptions = client . getOptions ( ) ;
4141 const beforeSendSpan = clientOptions . beforeSendSpan ;
4242
@@ -55,83 +55,17 @@ export const spanStreamingIntegration = defineIntegration(((userOptions?: Partia
5555 }
5656
5757 client . on ( 'enqueueSpan' , spanJSON => {
58- const spanTreeMapKey = getSpanTreeMapKey ( spanJSON ) ;
59- const spanBuffer = spanTreeMap . get ( spanTreeMapKey ) ;
60- if ( spanBuffer ) {
61- spanBuffer . add ( spanJSON ) ;
62- } else {
63- spanTreeMap . set ( spanTreeMapKey , new Set ( [ spanJSON ] ) ) ;
64- }
58+ buffer . addSpan ( spanJSON ) ;
6559 } ) ;
6660
6761 client . on ( 'afterSpanEnd' , span => {
6862 captureSpan ( span , client ) ;
6963 } ) ;
7064
71- // For now, we send all spans on local segment (root) span end.
72- // TODO: This will change once we have more concrete ideas about a universal SDK data buffer.
73- client . on ( 'afterSegmentSpanEnd' , segmentSpan => {
74- sendSegment ( segmentSpan , {
75- spanTreeMap,
76- client,
77- batchLimit : options . batchLimit ,
78- } ) ;
79- } ) ;
65+ // in addition to capturing the span, we also flush the trace when the segment
66+ // span ends to ensure things are sent timely. We never know when the browser
67+ // is closed, users navigate away, etc.
68+ client . on ( 'afterSegmentSpanEnd' , segmentSpan => buffer . flushTrace ( segmentSpan . spanContext ( ) . traceId ) ) ;
8069 } ,
8170 } ;
8271} ) satisfies IntegrationFn ) ;
83-
84- interface SpanProcessingOptions {
85- client : Client ;
86- spanTreeMap : Map < string , Set < SpanV2JSONWithSegmentRef > > ;
87- batchLimit : number ;
88- }
89-
90- /**
91- * Just the traceid alone isn't enough because there can be multiple span trees with the same traceid.
92- */
93- function getSpanTreeMapKey ( spanJSON : SpanV2JSONWithSegmentRef ) : string {
94- return `${ spanJSON . trace_id } -${ spanJSON . _segmentSpan ?. spanContext ( ) . spanId || spanJSON . span_id } ` ;
95- }
96-
97- function sendSegment ( segmentSpan : Span , { client, spanTreeMap, batchLimit } : SpanProcessingOptions ) : void {
98- const traceId = segmentSpan . spanContext ( ) . traceId ;
99- const segmentSpanId = segmentSpan . spanContext ( ) . spanId ;
100- const spanTreeMapKey = `${ traceId } -${ segmentSpanId } ` ;
101- const spansOfTrace = spanTreeMap . get ( spanTreeMapKey ) ;
102-
103- if ( ! spansOfTrace ?. size ) {
104- spanTreeMap . delete ( spanTreeMapKey ) ;
105- return ;
106- }
107-
108- // Apply beforeSendSpan callback and clean up segment span references
109- const finalSpans = Array . from ( spansOfTrace ) . map ( spanJSON => {
110- // Remove the segment span reference before processing
111- // eslint-disable-next-line @typescript-eslint/no-unused-vars
112- const { _segmentSpan, ...cleanSpanJSON } = spanJSON ;
113- return cleanSpanJSON ;
114- } ) ;
115-
116- const batches : SpanV2JSON [ ] [ ] = [ ] ;
117- for ( let i = 0 ; i < finalSpans . length ; i += batchLimit ) {
118- batches . push ( finalSpans . slice ( i , i + batchLimit ) ) ;
119- }
120-
121- DEBUG_BUILD && debug . log ( `Sending trace ${ traceId } in ${ batches . length } batch${ batches . length === 1 ? '' : 'es' } ` ) ;
122-
123- // Compute DSC from the segment span (passed as parameter)
124- const dsc = getDynamicSamplingContextFromSpan ( segmentSpan ) ;
125-
126- for ( const batch of batches ) {
127- const envelope = createSpanV2Envelope ( batch , dsc , client ) ;
128- // no need to handle client reports for network errors,
129- // buffer overflows or rate limiting here. All of this is handled
130- // by client and transport.
131- client . sendEnvelope ( envelope ) . then ( null , reason => {
132- DEBUG_BUILD && debug . error ( 'Error while sending span stream envelope:' , reason ) ;
133- } ) ;
134- }
135-
136- spanTreeMap . delete ( spanTreeMapKey ) ;
137- }
0 commit comments