Skip to content

Commit 3287684

Browse files
authored
promote ReplayManager to component, replacing Recorder (#1348)
1 parent f4a031f commit 3287684

13 files changed

+199
-224
lines changed

src/browser/core.js

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ import * as sharedTransforms from '../transforms.js';
1212
import * as predicates from './predicates.js';
1313
import * as sharedPredicates from '../predicates.js';
1414
import errorParser from '../errorParser.js';
15-
import recorderDefaults from './replay/defaults.js';
15+
import replayDefaults from './replay/defaults.js';
1616
import tracingDefaults from '../tracing/defaults.js';
17-
import ReplayManager from './replay/replayManager.js';
1817

1918
// Used to support global `Rollbar` instance.
2019
let _instance = null;
@@ -31,7 +30,7 @@ class Rollbar {
3130
this.scrub = this.components.scrub;
3231
const truncation = this.components.truncation;
3332
const Tracing = this.components.tracing;
34-
const Recorder = this.components.recorder;
33+
const ReplayManager = this.components.replayManager;
3534

3635
const transport = new Transport(truncation);
3736
const api = new API(this.options, transport, urllib, truncation);
@@ -43,18 +42,16 @@ class Rollbar {
4342
this.telemeter = new Telemeter(this.options, this.tracing);
4443
}
4544

46-
if (Recorder && _.isBrowser()) {
47-
const recorderOptions = this.options.recorder;
48-
this.recorder = new Recorder(recorderOptions);
45+
if (ReplayManager && _.isBrowser()) {
46+
const replayOptions = this.options.replay;
4947
this.replayManager = new ReplayManager({
50-
recorder: this.recorder,
51-
api: api,
5248
tracing: this.tracing,
5349
telemeter: this.telemeter,
50+
options: replayOptions,
5451
});
5552

56-
if (recorderOptions.enabled && recorderOptions.autoStart) {
57-
this.recorder.start();
53+
if (replayOptions.enabled && replayOptions.autoStart) {
54+
this.replayManager.recorder.start();
5855
}
5956
}
6057

@@ -129,7 +126,7 @@ class Rollbar {
129126
);
130127

131128
this.tracing?.configure(this.options);
132-
this.recorder?.configure(this.options);
129+
this.replayManager?.recorder?.configure(this.options);
133130
this.client.configure(this.options, payloadData);
134131
this.instrumenter?.configure(this.options);
135132
this.setupUnhandledCapture();
@@ -582,7 +579,7 @@ import {
582579
} from '../defaults.js';
583580
import browserDefaults from './defaults.js';
584581

585-
var defaultOptions = {
582+
const defaultOptions = {
586583
environment: 'unknown',
587584
version: version,
588585
scrubFields: browserDefaults.scrubFields,
@@ -599,7 +596,7 @@ var defaultOptions = {
599596
inspectAnonymousErrors: true,
600597
ignoreDuplicateErrors: true,
601598
wrapGlobalEventHandlers: false,
602-
recorder: recorderDefaults,
599+
replay: replayDefaults,
603600
tracing: tracingDefaults,
604601
};
605602

src/browser/replay/recorder.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,12 @@ export default class Recorder {
2727
* Creates a new Recorder instance for capturing DOM events
2828
*
2929
* @param {Object} options - Configuration options for the recorder
30-
* @param {Function} [recordFn=rrwebRecordFn] - The recording function to use
3130
*/
32-
constructor(options, recordFn = rrwebRecordFn) {
33-
if (!recordFn) {
34-
throw new TypeError("Expected 'recordFn' to be provided");
35-
}
36-
31+
constructor(options) {
3732
this.options = options;
38-
this._recordFn = recordFn;
33+
34+
// Tests inject a custom rrweb record function or mock.
35+
this._recordFn = options.recordFn ||rrwebRecordFn;
3936
}
4037

4138
get isRecording() {

src/browser/replay/replayManager.js

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import id from '../../tracing/id.js';
22
import logger from '../../logger.js';
3+
import Recorder from './recorder.js';
34
import ReplayPredicates from './replayPredicates.js';
45

56
/** @typedef {import('./recorder.js').BufferCursor} BufferCursor */
@@ -24,7 +25,6 @@ export default class ReplayManager {
2425
_map;
2526
/** @type {Recorder} */
2627
_recorder;
27-
_api;
2828
_tracing;
2929
_telemeter;
3030
_pendingLeading;
@@ -33,32 +33,22 @@ export default class ReplayManager {
3333
/**
3434
* Creates a new ReplayManager instance
3535
*
36-
* @param {Object} props - Configuration props
37-
* @param {Recorder} props.recorder - The recorder instance that dumps replay data into spans
38-
* @param {Object} props.api - The API instance used to send replay payloads to the backend
39-
* @param {Object} props.tracing - The tracing instance used to create spans and manage context
36+
* @param {Object} [props.tracing] - The tracing instance used to create spans and manage context
37+
* @param {Object} [props.telemeter] - Optional telemeter instance for capturing telemetry events
38+
* @param {Object} [props.options] - Configuration options
4039
*/
41-
constructor({ recorder, api, tracing, telemeter }) {
42-
if (!recorder) {
43-
throw new TypeError("Expected 'recorder' to be provided");
44-
}
45-
46-
if (!api) {
47-
throw new TypeError("Expected 'api' to be provided");
48-
}
49-
40+
constructor({ tracing, telemeter, options }) {
5041
if (!tracing) {
5142
throw new TypeError("Expected 'tracing' to be provided");
5243
}
5344

5445
this._map = new Map();
55-
this._recorder = recorder;
56-
this._api = api;
46+
this._recorder = new Recorder(options);
5747
this._tracing = tracing;
5848
this._telemeter = telemeter;
5949
this._pendingLeading = new Map();
6050
this._trailingStatus = new Map();
61-
this._predicates = new ReplayPredicates(recorder.options);
51+
this._predicates = new ReplayPredicates(options);
6252
}
6353

6454
/**
@@ -196,7 +186,7 @@ export default class ReplayManager {
196186
switch (trailingStatus) {
197187
case TrailingStatus.SENT:
198188
try {
199-
await this._api.postSpans(pendingContext.leadingPayload, {
189+
await this._tracing.exporter.post(pendingContext.leadingPayload, {
200190
'X-Rollbar-Replay-Id': replayId,
201191
});
202192
} catch (error) {
@@ -350,7 +340,7 @@ export default class ReplayManager {
350340
throw Error(`ReplayManager.send: No payload found for id: ${replayId}`);
351341
}
352342

353-
await this._api.postSpans(payload, { 'X-Rollbar-Replay-Id': replayId });
343+
await this._tracing.exporter.post(payload, { 'X-Rollbar-Replay-Id': replayId });
354344

355345
this._trailingStatus.set(replayId, TrailingStatus.SENT);
356346
await this._sendOrDiscardLeadingReplay(replayId);
@@ -413,6 +403,15 @@ export default class ReplayManager {
413403
return this._map.size;
414404
}
415405

406+
/**
407+
* Returns the Recorder instance used by this manager
408+
*
409+
* @returns {Recorder} The Recorder instance
410+
*/
411+
get recorder() {
412+
return this._recorder;
413+
}
414+
416415
/**
417416
* Clears all stored replays without sending them
418417
*/

src/browser/rollbar.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import wrapGlobals from './wrapGlobals.js';
55
import scrub from '../scrub.js';
66
import truncation from '../truncation.js';
77
import Tracing from '../tracing/tracing.js';
8-
import Recorder from './replay/recorder.js';
8+
import ReplayManager from './replay/replayManager.js';
99

1010
Rollbar.setComponents({
1111
telemeter: Telemeter,
@@ -14,7 +14,7 @@ Rollbar.setComponents({
1414
scrub: scrub,
1515
truncation: truncation,
1616
tracing: Tracing,
17-
recorder: Recorder,
17+
replayManager: ReplayManager,
1818
});
1919

2020
export default Rollbar;

test/browser.replay.recorder.test.js

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expect } from 'chai';
22
import sinon from 'sinon';
3+
import { record as rrwebRecordFn } from '@rrweb/record';
34
import { EventType } from '@rrweb/types';
45

56
import Recorder from '../src/browser/replay/recorder.js';
@@ -65,17 +66,16 @@ describe('Recorder', function () {
6566
});
6667
});
6768

68-
it('should throw error if no record function is passed', function () {
69-
expect(() => new Recorder({}, null)).to.throw(
70-
TypeError,
71-
"Expected 'recordFn' to be provided",
72-
);
69+
it('should use the default recorder if no record function is passed', function () {
70+
const recorder = new Recorder({});
71+
72+
expect(recorder._recordFn).to.equal(rrwebRecordFn);
7373
});
7474
});
7575

7676
describe('recording management', function () {
7777
it('should start recording correctly', function () {
78-
const recorder = new Recorder({ enabled: true }, recordFnStub);
78+
const recorder = new Recorder({ enabled: true, recordFn: recordFnStub });
7979
recorder.start();
8080

8181
expect(recorder.isRecording).to.be.true;
@@ -88,23 +88,23 @@ describe('Recorder', function () {
8888
});
8989

9090
it('should not start if already recording', function () {
91-
const recorder = new Recorder({ enabled: true }, recordFnStub);
91+
const recorder = new Recorder({ enabled: true, recordFn: recordFnStub });
9292
recorder.start();
9393
recorder.start();
9494

9595
expect(recordFnStub.calledOnce).to.be.true;
9696
});
9797

9898
it('should not start if disabled', function () {
99-
const recorder = new Recorder({ enabled: false }, recordFnStub);
99+
const recorder = new Recorder({ enabled: false, recordFn: recordFnStub });
100100
recorder.start();
101101

102102
expect(recorder.isRecording).to.be.false;
103103
expect(recordFnStub.called).to.be.false;
104104
});
105105

106106
it('should stop recording correctly', function () {
107-
const recorder = new Recorder({ enabled: true }, recordFnStub);
107+
const recorder = new Recorder({ enabled: true, recordFn: recordFnStub });
108108
recorder.start();
109109
recorder.stop();
110110

@@ -124,7 +124,7 @@ describe('Recorder', function () {
124124

125125
describe('event handling', function () {
126126
it('should handle events correctly', function () {
127-
const recorder = new Recorder({}, recordFnStub);
127+
const recorder = new Recorder({ recordFn: recordFnStub });
128128
recorder.start();
129129

130130
const event1 = { timestamp: 1000, type: 'event1', data: { a: 1 } };
@@ -166,7 +166,7 @@ describe('Recorder', function () {
166166
});
167167

168168
it('should be ready after first full snapshot', function () {
169-
const recorder = new Recorder({}, recordFnStub);
169+
const recorder = new Recorder({ recordFn: recordFnStub });
170170
recorder.start();
171171

172172
// First checkout
@@ -181,7 +181,7 @@ describe('Recorder', function () {
181181
});
182182

183183
it('should handle checkout events correctly', function () {
184-
const recorder = new Recorder({}, recordFnStub);
184+
const recorder = new Recorder({ recordFn: recordFnStub });
185185
recorder.start();
186186

187187
// First checkout
@@ -308,7 +308,7 @@ describe('Recorder', function () {
308308

309309
describe('dump functionality', function () {
310310
it('should create a span with events and return formatted payload', function () {
311-
const recorder = new Recorder({}, recordFnStub);
311+
const recorder = new Recorder({ recordFn: recordFnStub });
312312
recorder.start();
313313

314314
emitCallback({ timestamp: 1000, type: 'event1', data: { a: 1 } }, false);
@@ -355,7 +355,7 @@ describe('Recorder', function () {
355355
});
356356

357357
it('should create a span with the correct span name', function () {
358-
const recorder = new Recorder({}, recordFnStub);
358+
const recorder = new Recorder({ recordFn: recordFnStub });
359359
recorder.start();
360360

361361
emitCallback({ timestamp: 1000, type: 'event1', data: { a: 1 } }, false);
@@ -371,7 +371,7 @@ describe('Recorder', function () {
371371
});
372372

373373
it('should add events with the correct event name and replayId', function () {
374-
const recorder = new Recorder({}, recordFnStub);
374+
const recorder = new Recorder({ recordFn: recordFnStub });
375375
recorder.start();
376376

377377
emitCallback(
@@ -402,7 +402,7 @@ describe('Recorder', function () {
402402
});
403403

404404
it('should handle no events', function () {
405-
const recorder = new Recorder({}, recordFnStub);
405+
const recorder = new Recorder({ recordFn: recordFnStub });
406406

407407
expect(() => {
408408
recorder.exportRecordingSpan(mockTracing, {
@@ -417,7 +417,7 @@ describe('Recorder', function () {
417417

418418
describe('configure', function () {
419419
it('should update options', function () {
420-
const recorder = new Recorder({ enabled: true }, recordFnStub);
420+
const recorder = new Recorder({ enabled: true, recordFn: recordFnStub });
421421

422422
recorder.configure({ enabled: false, maxSeconds: 20 });
423423

@@ -449,7 +449,7 @@ describe('Recorder', function () {
449449
});
450450

451451
it('should stop recording if enabled set to false', function () {
452-
const recorder = new Recorder({ enabled: true }, recordFnStub);
452+
const recorder = new Recorder({ enabled: true, recordFn: recordFnStub });
453453
recorder.start();
454454

455455
expect(recorder.isRecording).to.be.true;

test/browser.rollbar.test.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ describe('Rollbar()', function () {
105105
var client = new (TestClientGen())();
106106
var options = {
107107
scrubFields: ['foobar'],
108-
recorder: {
108+
replay: {
109109
enabled: true,
110110
},
111111
tracing: {
@@ -116,8 +116,8 @@ describe('Rollbar()', function () {
116116

117117
expect(rollbar.options.scrubFields).to.contain('foobar');
118118
expect(rollbar.options.scrubFields).to.contain('password');
119-
expect(rollbar.options.recorder.enabled).to.be.true;
120-
expect(rollbar.options.recorder.triggers[0].level).to.eql([
119+
expect(rollbar.options.replay.enabled).to.be.true;
120+
expect(rollbar.options.replay.triggers[0].level).to.eql([
121121
'error',
122122
'critical',
123123
]);
@@ -869,7 +869,7 @@ describe('log', function () {
869869

870870
expect(body.data.body.trace.exception.message).to.eql('test error');
871871
expect(body.data.attributes).to.be.an('array');
872-
expect(body.data.attributes.length).to.eql(4);
872+
expect(body.data.attributes.length).to.eql(3);
873873
expect(body.data.attributes[0].key).to.eql('session_id');
874874
expect(body.data.attributes[0].value).to.match(/^[a-f0-9]{32}$/);
875875
expect(body.data.attributes[1].key).to.eql('span_id');

0 commit comments

Comments
 (0)