Skip to content

Commit 6acb3d4

Browse files
authored
ref(browser)!: Remove beforeSpanEnd callback in favor of beforeIdleSpanEnd hook (#22818)
#22554 introduced a new `beforeIdleSpanEnd` client hook. We can use it instead of the existing idle span's `beforeSpanEnd` callback. Breaking change because `startIdleSpan` is publicly exported, but I confirmed that we do not document this anywhere
1 parent 7269484 commit 6acb3d4

3 files changed

Lines changed: 50 additions & 46 deletions

File tree

packages/browser/src/tracing/browserTracingIntegration.ts

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -424,31 +424,6 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
424424
childSpanTimeout,
425425
// should wait for finish signal if it's a pageload transaction
426426
disableAutoFinish: isPageloadSpan,
427-
beforeSpanEnd: span => {
428-
addPerformanceEntries(span, {
429-
ignoreResourceSpans,
430-
spanStreamingEnabled: hasSpanStreamingEnabled(client),
431-
});
432-
setActiveIdleSpan(client, undefined);
433-
434-
// A trace should stay consistent over the entire timespan of one route - even after the pageload/navigation ended.
435-
// Only when another navigation happens, we want to create a new trace.
436-
// This way, e.g. errors that occur after the pageload span ended are still associated to the pageload trace.
437-
const scope = getCurrentScope();
438-
const oldPropagationContext = scope.getPropagationContext();
439-
440-
scope.setPropagationContext({
441-
...oldPropagationContext,
442-
traceId: idleSpan.spanContext().traceId,
443-
sampled: spanIsSampled(idleSpan),
444-
dsc: getDynamicSamplingContextFromSpan(span),
445-
});
446-
447-
if (isPageloadSpan) {
448-
// clean up the stored pageload span on the intergration.
449-
_pageloadSpan = undefined;
450-
}
451-
},
452427
trimIdleSpanEndTimestamp: !enableReportPageLoaded,
453428
});
454429

@@ -519,6 +494,39 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
519494
}
520495
}
521496

497+
client.on('beforeIdleSpanEnd', span => {
498+
// Interaction idle spans also flow through this hook, but the route bookkeeping below only
499+
// applies to the pageload/navigation span. We identify it by reference rather than by op:
500+
// only the route span is registered as the active idle span.
501+
if (getActiveIdleSpan(client) !== span) {
502+
return;
503+
}
504+
505+
addPerformanceEntries(span, {
506+
ignoreResourceSpans,
507+
spanStreamingEnabled: hasSpanStreamingEnabled(client),
508+
});
509+
setActiveIdleSpan(client, undefined);
510+
511+
// A trace should stay consistent over the entire timespan of one route - even after the pageload/navigation ended.
512+
// Only when another navigation happens, we want to create a new trace.
513+
// This way, e.g. errors that occur after the pageload span ended are still associated to the pageload trace.
514+
const scope = getCurrentScope();
515+
const oldPropagationContext = scope.getPropagationContext();
516+
517+
scope.setPropagationContext({
518+
...oldPropagationContext,
519+
traceId: span.spanContext().traceId,
520+
sampled: spanIsSampled(span),
521+
dsc: getDynamicSamplingContextFromSpan(span),
522+
});
523+
524+
if (_pageloadSpan === span) {
525+
// clean up the stored pageload span on the integration.
526+
_pageloadSpan = undefined;
527+
}
528+
});
529+
522530
client.on('startNavigationSpan', (startSpanOptions, navigationOptions) => {
523531
if (getClient() !== client) {
524532
return;

packages/core/src/tracing/idleSpan.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,6 @@ interface IdleSpanOptions {
7575
*/
7676
disableAutoFinish?: boolean;
7777

78-
/** Allows to configure a hook that is called when the idle span is ended, before it is processed. */
79-
beforeSpanEnd?: (span: Span) => void;
80-
8178
/**
8279
* If set to `true`, the idle span will be trimmed to the latest span end timestamp of its children.
8380
*
@@ -114,7 +111,6 @@ export function startIdleSpan(startSpanOptions: StartSpanOptions, options: Parti
114111
idleTimeout = TRACING_DEFAULTS.idleTimeout,
115112
finalTimeout = TRACING_DEFAULTS.finalTimeout,
116113
childSpanTimeout = TRACING_DEFAULTS.childSpanTimeout,
117-
beforeSpanEnd,
118114
trimIdleSpanEndTimestamp = true,
119115
} = options;
120116

@@ -142,10 +138,6 @@ export function startIdleSpan(startSpanOptions: StartSpanOptions, options: Parti
142138
apply(target, thisArg, args: Parameters<Span['end']>) {
143139
client.emit('beforeIdleSpanEnd', span);
144140

145-
if (beforeSpanEnd) {
146-
beforeSpanEnd(span);
147-
}
148-
149141
// If the span is non-recording, nothing more to do here...
150142
// This is the case if tracing is enabled but this specific span was not sampled
151143
if (spanIsNonRecordingSpan(thisArg)) {

packages/core/test/lib/tracing/idleSpan.test.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -214,21 +214,23 @@ describe('startIdleSpan', () => {
214214
});
215215
});
216216

217-
it('calls beforeSpanEnd callback before finishing', () => {
218-
const beforeSpanEnd = vi.fn();
219-
const idleSpan = startIdleSpan({ name: 'foo' }, { beforeSpanEnd });
217+
it('emits beforeIdleSpanEnd hook before finishing', () => {
218+
const beforeIdleSpanEnd = vi.fn();
219+
getClient()!.on('beforeIdleSpanEnd', beforeIdleSpanEnd);
220+
221+
const idleSpan = startIdleSpan({ name: 'foo' });
220222
expect(idleSpan).toBeDefined();
221223

222-
expect(beforeSpanEnd).not.toHaveBeenCalled();
224+
expect(beforeIdleSpanEnd).not.toHaveBeenCalled();
223225

224226
startSpan({ name: 'inner' }, () => {});
225227

226228
vi.runOnlyPendingTimers();
227-
expect(beforeSpanEnd).toHaveBeenCalledTimes(1);
228-
expect(beforeSpanEnd).toHaveBeenLastCalledWith(idleSpan);
229+
expect(beforeIdleSpanEnd).toHaveBeenCalledTimes(1);
230+
expect(beforeIdleSpanEnd).toHaveBeenLastCalledWith(idleSpan);
229231
});
230232

231-
it('allows to mutate idle span in beforeSpanEnd before it is sent', () => {
233+
it('allows to mutate idle span in beforeIdleSpanEnd before it is sent', () => {
232234
const transactions: Event[] = [];
233235
const beforeSendTransaction = vi.fn(event => {
234236
transactions.push(event);
@@ -246,16 +248,18 @@ describe('startIdleSpan', () => {
246248
// We want to accommodate a bit of drift there, so we ensure this starts earlier...
247249
const baseTimeInSeconds = Math.floor(Date.now() / 1000) - 9999;
248250

249-
const beforeSpanEnd = vi.fn((span: Span) => {
251+
const beforeIdleSpanEnd = vi.fn((span: Span) => {
250252
span.setAttribute('foo', 'bar');
251253
// Try adding a child here - we do this in browser tracing...
252-
const inner = startInactiveSpan({ name: 'from beforeSpanEnd', startTime: baseTimeInSeconds });
254+
const inner = startInactiveSpan({ name: 'from beforeIdleSpanEnd', startTime: baseTimeInSeconds });
253255
inner.end(baseTimeInSeconds + 1);
254256
});
255-
const idleSpan = startIdleSpan({ name: 'idle span', startTime: baseTimeInSeconds }, { beforeSpanEnd });
257+
client.on('beforeIdleSpanEnd', beforeIdleSpanEnd);
258+
259+
const idleSpan = startIdleSpan({ name: 'idle span', startTime: baseTimeInSeconds });
256260
expect(idleSpan).toBeDefined();
257261

258-
expect(beforeSpanEnd).not.toHaveBeenCalled();
262+
expect(beforeIdleSpanEnd).not.toHaveBeenCalled();
259263

260264
vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout + 1);
261265
vi.runOnlyPendingTimers();
@@ -270,7 +274,7 @@ describe('startIdleSpan', () => {
270274
const transaction = transactions[0]!;
271275

272276
expect(transaction.start_timestamp).toBe(baseTimeInSeconds);
273-
// It considers the end time of the span we added in beforeSpanEnd
277+
// It considers the end time of the span we added in beforeIdleSpanEnd
274278
expect(transaction.timestamp).toBe(baseTimeInSeconds + 1);
275279

276280
expect(transaction.contexts?.trace?.data).toEqual(
@@ -279,7 +283,7 @@ describe('startIdleSpan', () => {
279283
}),
280284
);
281285
expect(transaction.spans).toHaveLength(1);
282-
expect(transaction.spans).toEqual([expect.objectContaining({ description: 'from beforeSpanEnd' })]);
286+
expect(transaction.spans).toEqual([expect.objectContaining({ description: 'from beforeIdleSpanEnd' })]);
283287
});
284288

285289
it('runs beforeIdleSpanEnd before trimming the idle span', () => {

0 commit comments

Comments
 (0)