Skip to content

Commit c72a99c

Browse files
committed
feat(replay): Add traces_by_timestamp to replay event
In order to support moving our custom rrweb events to EAP, we need to send timestamps w/ trace ids so that we can identify which trace the event belongs to. In order to avoid breaking changes, we should not change the current type of trace_ids field in the replay event, instead we add a new field traces_by_timestamp
1 parent 0e15b3d commit c72a99c

File tree

5 files changed

+27
-9
lines changed

5 files changed

+27
-9
lines changed

packages/core/src/types-hoist/replay.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface ReplayEvent extends Event {
99
replay_start_timestamp?: number;
1010
error_ids: string[];
1111
trace_ids: string[];
12+
traces_by_timestamp: [number, string][];
1213
replay_id: string;
1314
segment_id: number;
1415
replay_type: ReplayRecordingMode;

packages/replay-internal/src/coreHandlers/handleAfterSendEvent.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ function handleTransactionEvent(replay: ReplayContainer, event: TransactionEvent
3737
// Collect traceIds in _context regardless of `recordingMode`
3838
// In error mode, _context gets cleared on every checkout
3939
// We limit to max. 100 transactions linked
40-
if (event.contexts?.trace?.trace_id && replayContext.traceIds.size < 100) {
41-
replayContext.traceIds.add(event.contexts.trace.trace_id);
40+
if (event.contexts?.trace?.trace_id && event.timestamp && replayContext.traceIds.size < 100) {
41+
replayContext.traceIds.add([event.timestamp, event.contexts.trace.trace_id]);
4242
}
4343
}
4444

packages/replay-internal/src/types/replay.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ export interface PopEventContext extends CommonEventContext {
335335
/**
336336
* List of Sentry trace ids that have occurred during a replay segment
337337
*/
338-
traceIds: Array<string>;
338+
traceIds: Array<[number, string]>;
339339
}
340340

341341
/**
@@ -348,9 +348,9 @@ export interface InternalEventContext extends CommonEventContext {
348348
errorIds: Set<string>;
349349

350350
/**
351-
* Set of Sentry trace ids that have occurred during a replay segment
351+
* Set of [timestamp, trace_id] tuples for Sentry traces that have occurred during a replay segment
352352
*/
353-
traceIds: Set<string>;
353+
traceIds: Set<[number, string]>;
354354
}
355355

356356
export type Sampled = false | 'session' | 'buffer';

packages/replay-internal/src/util/sendReplayRequest.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ export async function sendReplayRequest({
4242
replay_start_timestamp: initialTimestamp / 1000,
4343
timestamp: timestamp / 1000,
4444
error_ids: errorIds,
45-
trace_ids: traceIds,
45+
trace_ids: traceIds.map(([_ts, traceId]) => traceId),
46+
traces_by_timestamp: traceIds.map(([ts, traceId]) => [ts, traceId]),
4647
urls,
4748
replay_id: replayId,
4849
segment_id,

packages/replay-internal/test/integration/coreHandlers/handleAfterSendEvent.test.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,20 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => {
8484
handler(transaction4, { statusCode: undefined });
8585

8686
expect(Array.from(replay.getContext().errorIds)).toEqual([]);
87-
expect(Array.from(replay.getContext().traceIds)).toEqual(['tr2']);
87+
// traceIds is now a Set of [timestamp, trace_id] tuples
88+
const traceIds = Array.from(replay.getContext().traceIds);
89+
expect(traceIds).toHaveLength(1);
90+
expect(traceIds[0][1]).toBe('tr2');
91+
expect(typeof traceIds[0][0]).toBe('number');
8892

8993
// Does not affect error session
9094
await vi.advanceTimersToNextTimerAsync();
9195

9296
expect(Array.from(replay.getContext().errorIds)).toEqual([]);
93-
expect(Array.from(replay.getContext().traceIds)).toEqual(['tr2']);
97+
// Verify traceIds are still there after advancing timers
98+
const traceIdsAfter = Array.from(replay.getContext().traceIds);
99+
expect(traceIdsAfter).toHaveLength(1);
100+
expect(traceIdsAfter[0][1]).toBe('tr2');
94101
expect(replay.isEnabled()).toBe(true);
95102
expect(replay.isPaused()).toBe(false);
96103
expect(replay.recordingMode).toBe('buffer');
@@ -141,11 +148,20 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => {
141148
}
142149

143150
expect(Array.from(replay.getContext().errorIds)).toEqual([]);
144-
expect(Array.from(replay.getContext().traceIds)).toEqual(
151+
// traceIds is now a Set of [timestamp, trace_id] tuples
152+
const traceIds = Array.from(replay.getContext().traceIds);
153+
expect(traceIds).toHaveLength(100);
154+
// Check that all trace IDs are present
155+
expect(traceIds.map(([_timestamp, traceId]) => traceId)).toEqual(
145156
Array(100)
146157
.fill(undefined)
147158
.map((_, i) => `tr-${i}`),
148159
);
160+
// Check that all tuples have timestamps
161+
traceIds.forEach(([timestamp, _traceId]) => {
162+
expect(typeof timestamp).toBe('number');
163+
expect(timestamp).toBeGreaterThan(0);
164+
});
149165
});
150166

151167
it('flushes when in buffer mode', async () => {

0 commit comments

Comments
 (0)