Skip to content

Commit 30d746d

Browse files
committed
use SpanBuffer in browser
1 parent 704c13f commit 30d746d

File tree

3 files changed

+22
-77
lines changed

3 files changed

+22
-77
lines changed

packages/browser/src/client.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@ export class BrowserClient extends Client<BrowserClientOptions> {
129129
if (enableMetrics) {
130130
_INTERNAL_flushMetricsBuffer(this);
131131
}
132+
133+
// TODO: does anything speak against flushing here in general?
134+
// this would also allow us to let the logs and metric buffers listen
135+
// for client.on('flush'), meaning we don't have to explicitly call
136+
// them like above (?)
137+
// For now, this will flush the span buffer (besides errors, txns, etc).
138+
this.flush(2000).then(null, () => {});
132139
}
133140
});
134141
}

packages/browser/src/integrations/spanstreaming.ts

Lines changed: 8 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import {
66
defineIntegration,
77
getDynamicSamplingContextFromSpan,
88
isV2BeforeSendSpanCallback,
9+
SpanBuffer,
910
} from '@sentry/core';
1011
import { DEBUG_BUILD } from '../debug-build';
12+
import { WINDOW } from '../helpers';
1113

1214
export 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-
}

packages/core/src/spans/spanBuffer.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export class SpanBuffer {
5555
}
5656

5757
if (traceBucket.size >= this._maxSpanLimit) {
58-
this._flushTrace(traceId);
58+
this.flushTrace(traceId);
5959
this._debounceFlushInterval();
6060
}
6161
}
@@ -71,12 +71,16 @@ export class SpanBuffer {
7171
DEBUG_BUILD && debug.log(`Flushing span tree map with ${this._spanTreeMap.size} traces`);
7272

7373
this._spanTreeMap.forEach((_, traceId) => {
74-
this._flushTrace(traceId);
74+
this.flushTrace(traceId);
7575
});
7676
this._debounceFlushInterval();
7777
}
7878

79-
private _flushTrace(traceId: string): void {
79+
/**
80+
* Flush spans of a specific trace.
81+
* In contrast to {@link SpanBuffer.flush}, this method does not flush all traces, but only the one with the given traceId.
82+
*/
83+
public flushTrace(traceId: string): void {
8084
const traceBucket = this._spanTreeMap.get(traceId);
8185
if (!traceBucket) {
8286
return;

0 commit comments

Comments
 (0)