Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/browser/replay/recorder.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ export default class Recorder {
);
}

this._addEndEvent(recordingSpan, replayId);

recordingSpan.end();

return tracing.exporter.toPayload();
Expand Down Expand Up @@ -191,4 +193,21 @@ export default class Recorder {
})(event),
);
}

/**
* Helps the application correctly align playback by adding a noop event
* to the end of the recording.
**/
_addEndEvent(recordingSpan, replayId) {
recordingSpan.addEvent(

'rrweb-replay-events',
{
eventType: 5,
json: JSON.stringify({tag: "replay.end", payload: {}}),
Copy link

@shankj3 shankj3 Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't help us in the case of someone closing a tab, right?
Ie previous recording events have already been sent for this replay, then one more is going to be sent in the near future but tab is closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This span either gets sent in whole, or not at all. The purpose of adding this event is so that the player extends its playback until the triggering occurrence occurs. (It's almost like giving the occurrence an rrweb event.)

'rollbar.replay.id': replayId,
},
hrtime.fromMillis(Date.now()),
);
}
}
19 changes: 15 additions & 4 deletions test/browser.replay.recorder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ describe('Recorder', function () {
const result = recorder.dump(mockTracing, testReplayId);

expect(mockTracing.startSpan.calledOnce).to.be.true;
expect(mockSpan.addEvent.calledTwice).to.be.true;

// Event count includes the custom end event
expect(mockSpan.addEvent.calledThrice).to.be.true;
expect(mockSpan.setAttribute.calledOnce).to.be.true;
expect(mockSpan.setAttribute.calledWith('rollbar.replay.id', testReplayId)).to.be.true;

Expand All @@ -163,6 +165,11 @@ describe('Recorder', function () {
expect(secondCallData.eventType).to.equal('event2');
expect(JSON.parse(secondCallData.json)).to.deep.equal({ b: 2 });
expect(secondCallData['rollbar.replay.id']).to.equal(testReplayId);

const thirdCallData = mockSpan.addEvent.thirdCall.args[1];
expect(thirdCallData.eventType).to.equal(5);
expect(JSON.parse(thirdCallData.json)).to.deep.equal({tag: "replay.end", payload: {}});
expect(thirdCallData['rollbar.replay.id']).to.equal(testReplayId);
});

it('should handle checkout events correctly', function () {
Expand Down Expand Up @@ -228,7 +235,8 @@ describe('Recorder', function () {
expect(mockSpan.span.startTime).to.be.deep.equal([3, 50000000]); // otel time
expect(mockSpan.span.endTime).not.to.be.null;

expect(mockSpan.addEvent.callCount).to.equal(6);
// Event count includes the custom end event
expect(mockSpan.addEvent.callCount).to.equal(7);

[
{
Expand Down Expand Up @@ -306,7 +314,9 @@ describe('Recorder', function () {

expect(result).to.deep.equal([{ id: 'span1' }]);
expect(mockTracing.startSpan.calledOnce).to.be.true;
expect(mockSpan.addEvent.calledTwice).to.be.true;

// Event count includes the custom end event
expect(mockSpan.addEvent.calledThrice).to.be.true;
expect(mockSpan.end.calledOnce).to.be.true;
expect(mockTracing.exporter.toPayload.calledOnce).to.be.true;
});
Expand Down Expand Up @@ -341,7 +351,8 @@ describe('Recorder', function () {

recorder.dump(mockTracing, testReplayId);

expect(mockSpan.addEvent.callCount).to.equal(3);
// Event count includes the custom end event
expect(mockSpan.addEvent.callCount).to.equal(4);

for (let i = 0; i < mockSpan.addEvent.callCount; i++) {
const eventName = mockSpan.addEvent.getCall(i).args[0];
Expand Down
Loading