@multiplayer-app/session-recorder-react-native 1.3.16 → 1.3.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/module/config/constants.js +3 -0
- package/lib/module/config/constants.js.map +1 -1
- package/lib/module/config/defaults.js +5 -1
- package/lib/module/config/defaults.js.map +1 -1
- package/lib/module/config/session-recorder.js +5 -1
- package/lib/module/config/session-recorder.js.map +1 -1
- package/lib/module/otel/CrashBufferSpanProcessor.js +41 -0
- package/lib/module/otel/CrashBufferSpanProcessor.js.map +1 -0
- package/lib/module/otel/index.js +28 -9
- package/lib/module/otel/index.js.map +1 -1
- package/lib/module/recorder/index.js +15 -1
- package/lib/module/recorder/index.js.map +1 -1
- package/lib/module/services/api.service.js +24 -2
- package/lib/module/services/api.service.js.map +1 -1
- package/lib/module/services/crashBuffer.service.js +248 -0
- package/lib/module/services/crashBuffer.service.js.map +1 -0
- package/lib/module/services/socket.service.js +9 -2
- package/lib/module/services/socket.service.js.map +1 -1
- package/lib/module/session-recorder.js +152 -6
- package/lib/module/session-recorder.js.map +1 -1
- package/lib/module/types/session-recorder.js.map +1 -1
- package/lib/typescript/src/config/constants.d.ts +1 -0
- package/lib/typescript/src/config/constants.d.ts.map +1 -1
- package/lib/typescript/src/config/defaults.d.ts.map +1 -1
- package/lib/typescript/src/config/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/otel/CrashBufferSpanProcessor.d.ts +18 -0
- package/lib/typescript/src/otel/CrashBufferSpanProcessor.d.ts.map +1 -0
- package/lib/typescript/src/otel/index.d.ts +8 -0
- package/lib/typescript/src/otel/index.d.ts.map +1 -1
- package/lib/typescript/src/recorder/index.d.ts +8 -1
- package/lib/typescript/src/recorder/index.d.ts.map +1 -1
- package/lib/typescript/src/services/api.service.d.ts +27 -2
- package/lib/typescript/src/services/api.service.d.ts.map +1 -1
- package/lib/typescript/src/services/crashBuffer.service.d.ts +46 -0
- package/lib/typescript/src/services/crashBuffer.service.d.ts.map +1 -0
- package/lib/typescript/src/services/socket.service.d.ts +4 -3
- package/lib/typescript/src/services/socket.service.d.ts.map +1 -1
- package/lib/typescript/src/session-recorder.d.ts +8 -0
- package/lib/typescript/src/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/types/session-recorder.d.ts +18 -0
- package/lib/typescript/src/types/session-recorder.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/config/constants.ts +3 -0
- package/src/config/defaults.ts +5 -0
- package/src/config/session-recorder.ts +5 -0
- package/src/otel/CrashBufferSpanProcessor.ts +61 -0
- package/src/otel/index.ts +90 -34
- package/src/recorder/index.ts +30 -3
- package/src/services/api.service.ts +68 -13
- package/src/services/crashBuffer.service.ts +327 -0
- package/src/services/socket.service.ts +36 -22
- package/src/session-recorder.ts +226 -19
- package/src/types/session-recorder.ts +18 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Platform } from 'react-native';
|
|
4
|
+
import { SpanStatusCode } from '@opentelemetry/api';
|
|
5
|
+
|
|
6
|
+
// Safe import for AsyncStorage with web fallback
|
|
7
|
+
let AsyncStorage = null;
|
|
8
|
+
const isWeb = Platform.OS === 'web';
|
|
9
|
+
if (!isWeb) {
|
|
10
|
+
try {
|
|
11
|
+
AsyncStorage = require('@react-native-async-storage/async-storage').default;
|
|
12
|
+
} catch (_error) {
|
|
13
|
+
AsyncStorage = null;
|
|
14
|
+
}
|
|
15
|
+
} else {
|
|
16
|
+
AsyncStorage = {
|
|
17
|
+
getItem: _key => Promise.resolve(null),
|
|
18
|
+
setItem: (_key, _value) => Promise.resolve(undefined),
|
|
19
|
+
removeItem: _key => Promise.resolve(undefined),
|
|
20
|
+
multiRemove: _keys => Promise.resolve(undefined),
|
|
21
|
+
multiGet: _keys => Promise.resolve([]),
|
|
22
|
+
multiSet: _pairs => Promise.resolve(undefined)
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const INDEX_KEY = 'mp_crash_buffer_index_v1';
|
|
26
|
+
const ATTRS_KEY = 'mp_crash_buffer_attrs_v1';
|
|
27
|
+
const RECORD_PREFIX = 'mp_crash_buffer_rec_v1:';
|
|
28
|
+
const randomId = () => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
29
|
+
export class CrashBufferService {
|
|
30
|
+
static instance = null;
|
|
31
|
+
index = [];
|
|
32
|
+
indexLoaded = false;
|
|
33
|
+
lastPruneAt = 0;
|
|
34
|
+
opChain = Promise.resolve();
|
|
35
|
+
defaultWindowMs = 1 * 60 * 1000;
|
|
36
|
+
listeners = new Map();
|
|
37
|
+
static getInstance() {
|
|
38
|
+
if (!CrashBufferService.instance) {
|
|
39
|
+
CrashBufferService.instance = new CrashBufferService();
|
|
40
|
+
}
|
|
41
|
+
return CrashBufferService.instance;
|
|
42
|
+
}
|
|
43
|
+
enqueue(fn) {
|
|
44
|
+
const next = this.opChain.then(fn, fn);
|
|
45
|
+
// Preserve chain, but don't leak rejections.
|
|
46
|
+
this.opChain = next.then(() => undefined, () => undefined);
|
|
47
|
+
return next;
|
|
48
|
+
}
|
|
49
|
+
async ensureIndexLoaded() {
|
|
50
|
+
if (this.indexLoaded) return;
|
|
51
|
+
if (!AsyncStorage) {
|
|
52
|
+
this.indexLoaded = true;
|
|
53
|
+
this.index = [];
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const raw = await AsyncStorage.getItem(INDEX_KEY);
|
|
58
|
+
this.index = raw ? JSON.parse(raw) : [];
|
|
59
|
+
} catch (_e) {
|
|
60
|
+
this.index = [];
|
|
61
|
+
} finally {
|
|
62
|
+
this.indexLoaded = true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async setAttrs(attrs) {
|
|
66
|
+
return this.enqueue(async () => {
|
|
67
|
+
if (!AsyncStorage) return;
|
|
68
|
+
try {
|
|
69
|
+
await AsyncStorage.setItem(ATTRS_KEY, JSON.stringify(attrs || null));
|
|
70
|
+
} catch (_e) {
|
|
71
|
+
// best-effort
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
async appendEvent(payload, windowMs) {
|
|
76
|
+
return this.appendRecord('rrweb', payload.ts, payload, windowMs ?? this.defaultWindowMs);
|
|
77
|
+
}
|
|
78
|
+
async appendSpans(payload, windowMs) {
|
|
79
|
+
if (!payload.length) return;
|
|
80
|
+
const effectiveWindowMs = windowMs ?? this.defaultWindowMs;
|
|
81
|
+
return this.enqueue(async () => {
|
|
82
|
+
if (!AsyncStorage) return;
|
|
83
|
+
await this.ensureIndexLoaded();
|
|
84
|
+
const pairs = [];
|
|
85
|
+
let errorEvent = null;
|
|
86
|
+
for (const p of payload) {
|
|
87
|
+
const id = randomId();
|
|
88
|
+
const key = `${RECORD_PREFIX}${id}`;
|
|
89
|
+
const entry = {
|
|
90
|
+
id,
|
|
91
|
+
ts: p.ts,
|
|
92
|
+
kind: 'span'
|
|
93
|
+
};
|
|
94
|
+
this.index.push(entry);
|
|
95
|
+
pairs.push([key, JSON.stringify(p)]);
|
|
96
|
+
if (!errorEvent && p?.span?.status?.code === SpanStatusCode.ERROR) {
|
|
97
|
+
errorEvent = {
|
|
98
|
+
ts: p.ts,
|
|
99
|
+
span: p.span
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
await AsyncStorage.multiSet(pairs);
|
|
105
|
+
await AsyncStorage.setItem(INDEX_KEY, JSON.stringify(this.index));
|
|
106
|
+
} catch (_e) {
|
|
107
|
+
// best-effort
|
|
108
|
+
}
|
|
109
|
+
this.pruneSoon(effectiveWindowMs);
|
|
110
|
+
if (errorEvent) {
|
|
111
|
+
this._emit('error-span-appended', errorEvent);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
setDefaultWindowMs(windowMs) {
|
|
116
|
+
this.defaultWindowMs = Math.max(10_000, windowMs || 1 * 60 * 1000);
|
|
117
|
+
}
|
|
118
|
+
on(event, listener) {
|
|
119
|
+
const set = this.listeners.get(event) || new Set();
|
|
120
|
+
set.add(listener);
|
|
121
|
+
this.listeners.set(event, set);
|
|
122
|
+
return () => this.off(event, listener);
|
|
123
|
+
}
|
|
124
|
+
off(event, listener) {
|
|
125
|
+
const set = this.listeners.get(event);
|
|
126
|
+
if (!set) return;
|
|
127
|
+
set.delete(listener);
|
|
128
|
+
if (set.size === 0) this.listeners.delete(event);
|
|
129
|
+
}
|
|
130
|
+
_emit(event, payload) {
|
|
131
|
+
const set = this.listeners.get(event);
|
|
132
|
+
if (!set || set.size === 0) return;
|
|
133
|
+
for (const fn of Array.from(set)) {
|
|
134
|
+
try {
|
|
135
|
+
fn(payload);
|
|
136
|
+
} catch (_e) {
|
|
137
|
+
// never throw into app code
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async appendRecord(kind, ts, payload, windowMs) {
|
|
142
|
+
return this.enqueue(async () => {
|
|
143
|
+
if (!AsyncStorage) return;
|
|
144
|
+
await this.ensureIndexLoaded();
|
|
145
|
+
const id = randomId();
|
|
146
|
+
const key = `${RECORD_PREFIX}${id}`;
|
|
147
|
+
const entry = {
|
|
148
|
+
id,
|
|
149
|
+
ts,
|
|
150
|
+
kind
|
|
151
|
+
};
|
|
152
|
+
this.index.push(entry);
|
|
153
|
+
try {
|
|
154
|
+
await AsyncStorage.setItem(key, JSON.stringify(payload));
|
|
155
|
+
await AsyncStorage.setItem(INDEX_KEY, JSON.stringify(this.index));
|
|
156
|
+
} catch (_e) {
|
|
157
|
+
// best-effort
|
|
158
|
+
}
|
|
159
|
+
this.pruneSoon(windowMs);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
pruneSoon(windowMs) {
|
|
163
|
+
const now = Date.now();
|
|
164
|
+
if (now - this.lastPruneAt < 2000) return;
|
|
165
|
+
this.lastPruneAt = now;
|
|
166
|
+
const cutoff = Math.max(0, now - windowMs);
|
|
167
|
+
void this.pruneOlderThan(cutoff);
|
|
168
|
+
}
|
|
169
|
+
async pruneOlderThan(cutoffTs) {
|
|
170
|
+
return this.enqueue(async () => {
|
|
171
|
+
if (!AsyncStorage) return;
|
|
172
|
+
await this.ensureIndexLoaded();
|
|
173
|
+
const toRemove = this.index.filter(e => e.ts < cutoffTs);
|
|
174
|
+
if (toRemove.length === 0) return;
|
|
175
|
+
const removeKeys = toRemove.map(e => `${RECORD_PREFIX}${e.id}`);
|
|
176
|
+
this.index = this.index.filter(e => e.ts >= cutoffTs);
|
|
177
|
+
try {
|
|
178
|
+
await AsyncStorage.multiRemove(removeKeys);
|
|
179
|
+
await AsyncStorage.setItem(INDEX_KEY, JSON.stringify(this.index));
|
|
180
|
+
} catch (_e) {
|
|
181
|
+
// best-effort
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
async snapshot(windowMs, now = Date.now()) {
|
|
186
|
+
return this.enqueue(async () => {
|
|
187
|
+
await this.ensureIndexLoaded();
|
|
188
|
+
const effectiveWindowMs = windowMs ?? this.defaultWindowMs;
|
|
189
|
+
const toTs = now;
|
|
190
|
+
const fromTs = Math.max(0, toTs - effectiveWindowMs);
|
|
191
|
+
const entries = this.index.filter(e => e.ts >= fromTs && e.ts <= toTs);
|
|
192
|
+
const keys = entries.map(e => `${RECORD_PREFIX}${e.id}`);
|
|
193
|
+
let pairs = [];
|
|
194
|
+
try {
|
|
195
|
+
pairs = AsyncStorage ? await AsyncStorage.multiGet(keys) : [];
|
|
196
|
+
} catch (_e) {
|
|
197
|
+
pairs = [];
|
|
198
|
+
}
|
|
199
|
+
const byKey = new Map();
|
|
200
|
+
for (const [k, v] of pairs) {
|
|
201
|
+
if (!v) continue;
|
|
202
|
+
try {
|
|
203
|
+
byKey.set(k, JSON.parse(v));
|
|
204
|
+
} catch (_e) {
|
|
205
|
+
// ignore
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const rrwebEvents = [];
|
|
209
|
+
const otelSpans = [];
|
|
210
|
+
for (const e of entries.sort((a, b) => a.ts - b.ts)) {
|
|
211
|
+
const key = `${RECORD_PREFIX}${e.id}`;
|
|
212
|
+
const payload = byKey.get(key);
|
|
213
|
+
if (!payload) continue;
|
|
214
|
+
if (e.kind === 'rrweb') rrwebEvents.push(payload);
|
|
215
|
+
if (e.kind === 'span') otelSpans.push(payload);
|
|
216
|
+
}
|
|
217
|
+
let attrs = null;
|
|
218
|
+
try {
|
|
219
|
+
const raw = AsyncStorage ? await AsyncStorage.getItem(ATTRS_KEY) : null;
|
|
220
|
+
attrs = raw ? JSON.parse(raw) : null;
|
|
221
|
+
} catch (_e) {
|
|
222
|
+
attrs = null;
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
rrwebEvents,
|
|
226
|
+
otelSpans,
|
|
227
|
+
attrs,
|
|
228
|
+
windowMs: effectiveWindowMs,
|
|
229
|
+
fromTs,
|
|
230
|
+
toTs
|
|
231
|
+
};
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
async clear() {
|
|
235
|
+
return this.enqueue(async () => {
|
|
236
|
+
if (!AsyncStorage) return;
|
|
237
|
+
await this.ensureIndexLoaded();
|
|
238
|
+
const keys = this.index.map(e => `${RECORD_PREFIX}${e.id}`);
|
|
239
|
+
this.index = [];
|
|
240
|
+
try {
|
|
241
|
+
await AsyncStorage.multiRemove([INDEX_KEY, ATTRS_KEY, ...keys]);
|
|
242
|
+
} catch (_e) {
|
|
243
|
+
// best-effort
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=crashBuffer.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["Platform","SpanStatusCode","AsyncStorage","isWeb","OS","require","default","_error","getItem","_key","Promise","resolve","setItem","_value","undefined","removeItem","multiRemove","_keys","multiGet","multiSet","_pairs","INDEX_KEY","ATTRS_KEY","RECORD_PREFIX","randomId","Date","now","toString","Math","random","slice","CrashBufferService","instance","index","indexLoaded","lastPruneAt","opChain","defaultWindowMs","listeners","Map","getInstance","enqueue","fn","next","then","ensureIndexLoaded","raw","JSON","parse","_e","setAttrs","attrs","stringify","appendEvent","payload","windowMs","appendRecord","ts","appendSpans","length","effectiveWindowMs","pairs","errorEvent","p","id","key","entry","kind","push","span","status","code","ERROR","pruneSoon","_emit","setDefaultWindowMs","max","on","event","listener","set","get","Set","add","off","delete","size","Array","from","cutoff","pruneOlderThan","cutoffTs","toRemove","filter","e","removeKeys","map","snapshot","toTs","fromTs","entries","keys","byKey","k","v","rrwebEvents","otelSpans","sort","a","b","clear"],"sourceRoot":"../../../src","sources":["services/crashBuffer.service.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import io from 'socket.io-client';
|
|
4
4
|
import { Observable } from 'lib0/observable';
|
|
5
5
|
import { logger } from "../utils/index.js";
|
|
6
|
-
import { SESSION_ADD_EVENT, SESSION_AUTO_CREATED, SESSION_STOPPED_EVENT, SESSION_SUBSCRIBE_EVENT, SESSION_UNSUBSCRIBE_EVENT, SOCKET_SET_USER_EVENT, REMOTE_SESSION_RECORDING_START, REMOTE_SESSION_RECORDING_STOP, SESSION_STARTED_EVENT } from "../config/index.js";
|
|
6
|
+
import { SESSION_ADD_EVENT, SESSION_AUTO_CREATED, SESSION_STOPPED_EVENT, SESSION_SUBSCRIBE_EVENT, SESSION_UNSUBSCRIBE_EVENT, SOCKET_SET_USER_EVENT, REMOTE_SESSION_RECORDING_START, REMOTE_SESSION_RECORDING_STOP, SESSION_STARTED_EVENT, SESSION_SAVE_BUFFER_EVENT } from "../config/index.js";
|
|
7
|
+
import { ATTR_MULTIPLAYER_SESSION_CLIENT_ID } from '@multiplayer-app/session-recorder-common';
|
|
7
8
|
const MAX_RECONNECTION_ATTEMPTS = 2;
|
|
8
9
|
export class SocketService extends Observable {
|
|
9
10
|
socket = null;
|
|
@@ -66,7 +67,10 @@ export class SocketService extends Observable {
|
|
|
66
67
|
this.socket = io(this.options.socketUrl, {
|
|
67
68
|
path: '/v0/radar/ws',
|
|
68
69
|
auth: {
|
|
69
|
-
'x-api-key': this.options.apiKey
|
|
70
|
+
'x-api-key': this.options.apiKey,
|
|
71
|
+
...(this.options.clientId ? {
|
|
72
|
+
[ATTR_MULTIPLAYER_SESSION_CLIENT_ID]: this.options.clientId
|
|
73
|
+
} : {})
|
|
70
74
|
},
|
|
71
75
|
reconnectionAttempts: 2,
|
|
72
76
|
transports: ['websocket']
|
|
@@ -100,6 +104,9 @@ export class SocketService extends Observable {
|
|
|
100
104
|
this.socket.on(REMOTE_SESSION_RECORDING_STOP, data => {
|
|
101
105
|
this.emit(REMOTE_SESSION_RECORDING_STOP, [data]);
|
|
102
106
|
});
|
|
107
|
+
this.socket.on(SESSION_SAVE_BUFFER_EVENT, data => {
|
|
108
|
+
this.emit(SESSION_SAVE_BUFFER_EVENT, [data]);
|
|
109
|
+
});
|
|
103
110
|
}
|
|
104
111
|
checkReconnectionAttempts() {
|
|
105
112
|
if (this.attempts >= MAX_RECONNECTION_ATTEMPTS) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["io","Observable","logger","SESSION_ADD_EVENT","SESSION_AUTO_CREATED","SESSION_STOPPED_EVENT","SESSION_SUBSCRIBE_EVENT","SESSION_UNSUBSCRIBE_EVENT","SOCKET_SET_USER_EVENT","REMOTE_SESSION_RECORDING_START","REMOTE_SESSION_RECORDING_STOP","SESSION_STARTED_EVENT","MAX_RECONNECTION_ATTEMPTS","SocketService","socket","queue","isConnecting","isConnected","attempts","sessionId","constructor","options","apiKey","socketUrl","keepAlive","init","config","_initConnection","updateConfigs","hasChanges","Object","keys","some","key","typedKey","undefined","connected","close","then","path","auth","reconnectionAttempts","transports","on","info","flushQueue","_err","err","checkReconnectionAttempts","error","data","emit","emitSocketEvent","name","push","length","event","shift","send","subscribeToSession","session","shortId","_id","payload","projectId","project","workspaceId","workspace","debugSessionId","sessionType","creationType","unsubscribeFromSession","stopSession","setUser","userAttributes","Promise","resolve","setTimeout","disconnect"],"sourceRoot":"../../../src","sources":["services/socket.service.ts"],"mappings":";;AAAA,OAAOA,EAAE,MAAkB,kBAAkB;AAC7C,SAASC,UAAU,QAAQ,iBAAiB;AAE5C,SAASC,MAAM,QAAQ,mBAAU;AAEjC,SACEC,iBAAiB,EACjBC,oBAAoB,EACpBC,qBAAqB,EACrBC,uBAAuB,EACvBC,yBAAyB,EACzBC,qBAAqB,EACrBC,8BAA8B,EAC9BC,6BAA6B,EAC7BC,qBAAqB,
|
|
1
|
+
{"version":3,"names":["io","Observable","logger","SESSION_ADD_EVENT","SESSION_AUTO_CREATED","SESSION_STOPPED_EVENT","SESSION_SUBSCRIBE_EVENT","SESSION_UNSUBSCRIBE_EVENT","SOCKET_SET_USER_EVENT","REMOTE_SESSION_RECORDING_START","REMOTE_SESSION_RECORDING_STOP","SESSION_STARTED_EVENT","SESSION_SAVE_BUFFER_EVENT","ATTR_MULTIPLAYER_SESSION_CLIENT_ID","MAX_RECONNECTION_ATTEMPTS","SocketService","socket","queue","isConnecting","isConnected","attempts","sessionId","constructor","options","apiKey","socketUrl","keepAlive","init","config","_initConnection","updateConfigs","hasChanges","Object","keys","some","key","typedKey","undefined","connected","close","then","path","auth","clientId","reconnectionAttempts","transports","on","info","flushQueue","_err","err","checkReconnectionAttempts","error","data","emit","emitSocketEvent","name","push","length","event","shift","send","subscribeToSession","session","shortId","_id","payload","projectId","project","workspaceId","workspace","debugSessionId","sessionType","creationType","unsubscribeFromSession","stopSession","setUser","userAttributes","Promise","resolve","setTimeout","disconnect"],"sourceRoot":"../../../src","sources":["services/socket.service.ts"],"mappings":";;AAAA,OAAOA,EAAE,MAAkB,kBAAkB;AAC7C,SAASC,UAAU,QAAQ,iBAAiB;AAE5C,SAASC,MAAM,QAAQ,mBAAU;AAEjC,SACEC,iBAAiB,EACjBC,oBAAoB,EACpBC,qBAAqB,EACrBC,uBAAuB,EACvBC,yBAAyB,EACzBC,qBAAqB,EACrBC,8BAA8B,EAC9BC,6BAA6B,EAC7BC,qBAAqB,EACrBC,yBAAyB,QACpB,oBAAW;AAClB,SAGEC,kCAAkC,QAC7B,0CAA0C;AAEjD,MAAMC,yBAAyB,GAAG,CAAC;AAgBnC,OAAO,MAAMC,aAAa,SAASd,UAAU,CAAsB;EACzDe,MAAM,GAAkB,IAAI;EAC5BC,KAAK,GAAU,EAAE;EACjBC,YAAY,GAAY,KAAK;EAC7BC,WAAW,GAAY,KAAK;EAC5BC,QAAQ,GAAW,CAAC;EACpBC,SAAS,GAAkB,IAAI;EAGvCC,WAAWA,CAAA,EAAG;IACZ,KAAK,CAAC,CAAC;IACP,IAAI,CAACC,OAAO,GAAG;MACbC,MAAM,EAAE,EAAE;MACVC,SAAS,EAAE,EAAE;MACbC,SAAS,EAAE;IACb,CAAC;EACH;;EAEA;AACF;AACA;AACA;EACSC,IAAIA,CAACC,MAA4B,EAAQ;IAC9C,IAAI,CAACL,OAAO,GAAG;MACb,GAAG,IAAI,CAACA,OAAO;MACf,GAAGK;IACL,CAAC;IACD,IACE,IAAI,CAACL,OAAO,CAACG,SAAS,IACtB,IAAI,CAACH,OAAO,CAACE,SAAS,IACtB,IAAI,CAACF,OAAO,CAACC,MAAM,EACnB;MACA,IAAI,CAACK,eAAe,CAAC,CAAC;IACxB;EACF;;EAEA;AACF;AACA;AACA;EACSC,aAAaA,CAACF,MAAqC,EAAQ;IAChE;IACA,MAAMG,UAAU,GAAGC,MAAM,CAACC,IAAI,CAACL,MAAM,CAAC,CAACM,IAAI,CAAEC,GAAG,IAAK;MACnD,MAAMC,QAAQ,GAAGD,GAAiC;MAClD,OACEP,MAAM,CAACQ,QAAQ,CAAC,KAAKC,SAAS,IAC9BT,MAAM,CAACQ,QAAQ,CAAC,KAAK,IAAI,CAACb,OAAO,CAACa,QAAQ,CAAC;IAE/C,CAAC,CAAC;IAEF,IAAIL,UAAU,EAAE;MACd,IAAI,CAACR,OAAO,GAAG;QAAE,GAAG,IAAI,CAACA,OAAO;QAAE,GAAGK;MAAO,CAAC;MAC7C,IAAI,IAAI,CAACZ,MAAM,EAAEsB,SAAS,EAAE;QAC1B,IAAI,CAACC,KAAK,CAAC,CAAC,CAACC,IAAI,CAAC,MAAM;UACtB,IACE,IAAI,CAACjB,OAAO,CAACG,SAAS,IACtB,IAAI,CAACH,OAAO,CAACE,SAAS,IACtB,IAAI,CAACF,OAAO,CAACC,MAAM,EACnB;YACA,IAAI,CAACK,eAAe,CAAC,CAAC;UACxB;QACF,CAAC,CAAC;MACJ;IACF;EACF;EAEQA,eAAeA,CAAA,EAAS;IAC9B,IAAI,IAAI,CAACX,YAAY,IAAI,IAAI,CAACC,WAAW,EAAE;IAC3C,IAAI,CAACC,QAAQ,EAAE;IACf,IAAI,CAACF,YAAY,GAAG,IAAI;IACxB,IAAI,CAACF,MAAM,GAAGhB,EAAE,CAAC,IAAI,CAACuB,OAAO,CAACE,SAAS,EAAE;MACvCgB,IAAI,EAAE,cAAc;MACpBC,IAAI,EAAE;QACJ,WAAW,EAAE,IAAI,CAACnB,OAAO,CAACC,MAAM;QAChC,IAAI,IAAI,CAACD,OAAO,CAACoB,QAAQ,GACrB;UAAE,CAAC9B,kCAAkC,GAAG,IAAI,CAACU,OAAO,CAACoB;QAAS,CAAC,GAC/D,CAAC,CAAC;MACR,CAAC;MACDC,oBAAoB,EAAE,CAAC;MACvBC,UAAU,EAAE,CAAC,WAAW;IAC1B,CAAC,CAAC;IAEF,IAAI,CAAC7B,MAAM,CAAC8B,EAAE,CAAC,OAAO,EAAE,MAAM;MAC5B,IAAI,CAAC5B,YAAY,GAAG,KAAK;MACzB,IAAI,CAACC,WAAW,GAAG,IAAI;MACvBjB,MAAM,CAAC6C,IAAI,CAAC,eAAe,EAAE,qBAAqB,CAAC;MACnD,IAAI,CAACC,UAAU,CAAC,CAAC;IACnB,CAAC,CAAC;IAEF,IAAI,CAAChC,MAAM,CAAC8B,EAAE,CAAC,YAAY,EAAGG,IAAS,IAAK;MAC1C,IAAI,CAAC/B,YAAY,GAAG,KAAK;MACzB,IAAI,CAACC,WAAW,GAAG,KAAK;MACxBjB,MAAM,CAAC6C,IAAI,CAAC,eAAe,EAAE,0BAA0B,CAAC;IAC1D,CAAC,CAAC;IAEF,IAAI,CAAC/B,MAAM,CAAC8B,EAAE,CAAC,eAAe,EAAGI,GAAQ,IAAK;MAC5C,IAAI,CAAChC,YAAY,GAAG,KAAK;MACzB,IAAI,CAACC,WAAW,GAAG,KAAK;MACxB,IAAI,CAACgC,yBAAyB,CAAC,CAAC;MAChCjD,MAAM,CAACkD,KAAK,CAAC,eAAe,EAAE,4BAA4B,EAAEF,GAAG,CAAC;IAClE,CAAC,CAAC;IAEF,IAAI,CAAClC,MAAM,CAAC8B,EAAE,CAACzC,qBAAqB,EAAGgD,IAAS,IAAK;MACnD,IAAI,CAACC,IAAI,CAACjD,qBAAqB,EAAE,CAACgD,IAAI,CAAC,CAAC;IAC1C,CAAC,CAAC;IAEF,IAAI,CAACrC,MAAM,CAAC8B,EAAE,CAAC1C,oBAAoB,EAAGiD,IAAS,IAAK;MAClD,IAAI,CAACC,IAAI,CAAClD,oBAAoB,EAAE,CAACiD,IAAI,CAAC,CAAC;IACzC,CAAC,CAAC;IAEF,IAAI,CAACrC,MAAM,CAAC8B,EAAE,CAACrC,8BAA8B,EAAG4C,IAAS,IAAK;MAC5D,IAAI,CAACC,IAAI,CAAC7C,8BAA8B,EAAE,CAAC4C,IAAI,CAAC,CAAC;IACnD,CAAC,CAAC;IAEF,IAAI,CAACrC,MAAM,CAAC8B,EAAE,CAACpC,6BAA6B,EAAG2C,IAAS,IAAK;MAC3D,IAAI,CAACC,IAAI,CAAC5C,6BAA6B,EAAE,CAAC2C,IAAI,CAAC,CAAC;IAClD,CAAC,CAAC;IAEF,IAAI,CAACrC,MAAM,CAAC8B,EAAE,CAAClC,yBAAyB,EAAGyC,IAAS,IAAK;MACvD,IAAI,CAACC,IAAI,CAAC1C,yBAAyB,EAAE,CAACyC,IAAI,CAAC,CAAC;IAC9C,CAAC,CAAC;EACJ;EAEQF,yBAAyBA,CAAA,EAAS;IACxC,IAAI,IAAI,CAAC/B,QAAQ,IAAIN,yBAAyB,EAAE;MAC9C,IAAI,CAACkC,UAAU,CAAC,CAAC;IACnB;EACF;EAEQO,eAAeA,CAACC,IAAY,EAAEH,IAAS,EAAQ;IACrD,IAAI,IAAI,CAACrC,MAAM,IAAI,IAAI,CAACG,WAAW,EAAE;MACnC,IAAI,CAACH,MAAM,CAACsC,IAAI,CAACE,IAAI,EAAEH,IAAI,CAAC;IAC9B,CAAC,MAAM;MACL,IAAI,CAACpC,KAAK,CAACwC,IAAI,CAAC;QAAEJ,IAAI;QAAEG;MAAK,CAAC,CAAC;MAC/B,IAAI,CAAC3B,eAAe,CAAC,CAAC;IACxB;EACF;EAEQmB,UAAUA,CAAA,EAAS;IACzB,OAAO,IAAI,CAAC/B,KAAK,CAACyC,MAAM,GAAG,CAAC,IAAI,IAAI,CAACvC,WAAW,EAAE;MAChD,MAAMwC,KAAK,GAAG,IAAI,CAAC1C,KAAK,CAAC2C,KAAK,CAAC,CAAC;MAChC,IAAI,CAACD,KAAK,EAAE;MAEZ,IAAI,IAAI,CAAC3C,MAAM,IAAI,IAAI,CAACG,WAAW,EAAE;QACnC,IAAI,CAACH,MAAM,CAACsC,IAAI,CAACK,KAAK,CAACH,IAAI,EAAEG,KAAK,CAACN,IAAI,CAAC;MAC1C;IACF;EACF;EAEOQ,IAAIA,CAACF,KAAU,EAAQ;IAC5B,IAAI,CAACJ,eAAe,CAACpD,iBAAiB,EAAEwD,KAAK,CAAC;EAChD;EAEOG,kBAAkBA,CAACC,OAAiB,EAAQ;IACjD,IAAI,CAAC1C,SAAS,GAAG0C,OAAO,CAACC,OAAO,IAAID,OAAO,CAACE,GAAG;IAC/C,MAAMC,OAAO,GAAG;MACdC,SAAS,EAAEJ,OAAO,CAACK,OAAO;MAC1BC,WAAW,EAAEN,OAAO,CAACO,SAAS;MAC9BC,cAAc,EAAE,IAAI,CAAClD,SAAS;MAC9BmD,WAAW,EAAET,OAAO,CAACU;IACvB,CAAC;IACD,IAAI,CAAClB,eAAe,CAACjD,uBAAuB,EAAE4D,OAAO,CAAC;IACtD;IACA,IAAI,CAACX,eAAe,CAAC5C,qBAAqB,EAAE;MAC1C4D,cAAc,EAAER,OAAO,CAACE;IAC1B,CAAC,CAAC;EACJ;EAEOS,sBAAsBA,CAACC,WAAqB,EAAE;IACnD,IAAI,IAAI,CAACtD,SAAS,EAAE;MAClB,IAAI,CAACkC,eAAe,CAAChD,yBAAyB,EAAE;QAC9CgE,cAAc,EAAE,IAAI,CAAClD;MACvB,CAAC,CAAC;MACF,IAAIsD,WAAW,EAAE;QACf,IAAI,CAACpB,eAAe,CAAClD,qBAAqB,EAAE,CAAC,CAAC,CAAC;MACjD;IACF;EACF;EAEOuE,OAAOA,CAACC,cAAsC,EAAQ;IAC3D,IAAI,CAACtB,eAAe,CAAC/C,qBAAqB,EAAEqE,cAAc,CAAC;EAC7D;EAEOtC,KAAKA,CAAA,EAAkB;IAC5B,OAAO,IAAIuC,OAAO,CAAEC,OAAO,IAAK;MAC9B,IAAI,IAAI,CAAC/D,MAAM,EAAEsB,SAAS,EAAE;QAC1B0C,UAAU,CAAC,MAAM;UACf,IAAI,CAACN,sBAAsB,CAAC,CAAC;UAC7B,IAAI,CAACtD,QAAQ,GAAG,CAAC;UACjB,IAAI,CAACD,WAAW,GAAG,KAAK;UACxB,IAAI,CAACD,YAAY,GAAG,KAAK;UACzB,IAAI,CAACF,MAAM,EAAEiE,UAAU,CAAC,CAAC;UACzB,IAAI,CAACjE,MAAM,GAAG,IAAI;UAClB+D,OAAO,CAAC,CAAC;QACX,CAAC,EAAE,GAAG,CAAC;MACT,CAAC,MAAM;QACLA,OAAO,CAAC,CAAC;MACX;IACF,CAAC,CAAC;EACJ;AACF","ignoreList":[]}
|
|
@@ -6,12 +6,13 @@ import { TracerReactNativeSDK } from "./otel/index.js";
|
|
|
6
6
|
import { RecorderReactNativeSDK } from "./recorder/index.js";
|
|
7
7
|
import { logger } from "./utils/index.js";
|
|
8
8
|
import { SessionState } from "./types/index.js";
|
|
9
|
-
import { SESSION_STOPPED_EVENT, REMOTE_SESSION_RECORDING_START, REMOTE_SESSION_RECORDING_STOP } from "./config/index.js";
|
|
9
|
+
import { SESSION_STOPPED_EVENT, REMOTE_SESSION_RECORDING_START, REMOTE_SESSION_RECORDING_STOP, SESSION_SAVE_BUFFER_EVENT } from "./config/index.js";
|
|
10
10
|
import { getFormattedDate, isSessionActive, getNavigatorInfo } from "./utils/index.js";
|
|
11
11
|
import { setShouldRecordHttpData, setMaxCapturingHttpPayloadSize } from "./patch/index.js";
|
|
12
12
|
import { BASE_CONFIG, getSessionRecorderConfig } from "./config/index.js";
|
|
13
13
|
import { StorageService } from "./services/storage.service.js";
|
|
14
14
|
import { NetworkService } from "./services/network.service.js";
|
|
15
|
+
import { CrashBufferService } from "./services/crashBuffer.service.js";
|
|
15
16
|
import { ApiService } from "./services/api.service.js";
|
|
16
17
|
import { SocketService } from "./services/socket.service.js";
|
|
17
18
|
class SessionRecorder extends Observable {
|
|
@@ -21,6 +22,8 @@ class SessionRecorder extends Observable {
|
|
|
21
22
|
_recorder = new RecorderReactNativeSDK();
|
|
22
23
|
_storageService = StorageService.getInstance();
|
|
23
24
|
_networkService = NetworkService.getInstance();
|
|
25
|
+
_crashBuffer = CrashBufferService.getInstance();
|
|
26
|
+
_isFlushingBuffer = false;
|
|
24
27
|
_startRequestController = null;
|
|
25
28
|
|
|
26
29
|
// Whether the session recorder is initialized
|
|
@@ -131,6 +134,95 @@ class SessionRecorder extends Observable {
|
|
|
131
134
|
this.error = e?.message || 'Failed to capture exception';
|
|
132
135
|
}
|
|
133
136
|
}
|
|
137
|
+
async flushBuffer(payload) {
|
|
138
|
+
if (!this._configs?.buffering?.enabled) return null;
|
|
139
|
+
if (this._isFlushingBuffer) return null;
|
|
140
|
+
if (this.sessionState !== SessionState.stopped || this.sessionId) return null;
|
|
141
|
+
const windowMs = Math.max(10_000, (this._configs.buffering.windowMinutes || 2) * 60 * 1000);
|
|
142
|
+
this._isFlushingBuffer = true;
|
|
143
|
+
try {
|
|
144
|
+
const reason = payload?.reason || 'manual';
|
|
145
|
+
await this._crashBuffer.setAttrs({
|
|
146
|
+
sessionAttributes: this.sessionAttributes,
|
|
147
|
+
resourceAttributes: getNavigatorInfo(),
|
|
148
|
+
userAttributes: this._userAttributes
|
|
149
|
+
});
|
|
150
|
+
const snapshot = await this._crashBuffer.snapshot(windowMs);
|
|
151
|
+
if (snapshot.rrwebEvents.length === 0 && snapshot.otelSpans.length === 0) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
const request = {
|
|
155
|
+
name: `${this._configs.application} ${getFormattedDate(new Date())}`,
|
|
156
|
+
stoppedAt: new Date().toISOString(),
|
|
157
|
+
sessionAttributes: this.sessionAttributes,
|
|
158
|
+
resourceAttributes: getNavigatorInfo(),
|
|
159
|
+
...(this._userAttributes ? {
|
|
160
|
+
userAttributes: this._userAttributes
|
|
161
|
+
} : {}),
|
|
162
|
+
debugSessionData: {
|
|
163
|
+
meta: {
|
|
164
|
+
reason,
|
|
165
|
+
windowMs: snapshot.windowMs,
|
|
166
|
+
fromTs: snapshot.fromTs,
|
|
167
|
+
toTs: snapshot.toTs
|
|
168
|
+
},
|
|
169
|
+
events: snapshot.rrwebEvents,
|
|
170
|
+
spans: snapshot.otelSpans.map(s => s.span),
|
|
171
|
+
attrs: snapshot.attrs
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
try {
|
|
175
|
+
const res = await this._apiService.startSession(request);
|
|
176
|
+
await this._crashBuffer.clear();
|
|
177
|
+
return res;
|
|
178
|
+
} catch (_e) {
|
|
179
|
+
// swallow: flush is best-effort; never throw into app code
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
} finally {
|
|
183
|
+
this._isFlushingBuffer = false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async _flushBuffer(sessionId) {
|
|
187
|
+
if (!this._configs?.buffering?.enabled) return null;
|
|
188
|
+
if (this._isFlushingBuffer) return null;
|
|
189
|
+
if (this.sessionState !== SessionState.stopped || this.sessionId) return null;
|
|
190
|
+
const windowMs = Math.max(10_000, (this._configs.buffering.windowMinutes || 2) * 60 * 1000);
|
|
191
|
+
this._isFlushingBuffer = true;
|
|
192
|
+
try {
|
|
193
|
+
const snapshot = await this._crashBuffer.snapshot(windowMs);
|
|
194
|
+
if (snapshot.rrwebEvents.length === 0 && snapshot.otelSpans.length === 0) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
const spans = snapshot.otelSpans.map(s => s.span);
|
|
198
|
+
const events = snapshot.rrwebEvents.map(e => e.event);
|
|
199
|
+
await Promise.all([this._tracer.exportTraces(spans), this._apiService.exportEvents(sessionId, {
|
|
200
|
+
events
|
|
201
|
+
}), this._apiService.updateSessionAttributes(sessionId, {
|
|
202
|
+
name: this._getSessionName(),
|
|
203
|
+
sessionAttributes: this.sessionAttributes,
|
|
204
|
+
resourceAttributes: getNavigatorInfo(),
|
|
205
|
+
userAttributes: this._userAttributes || undefined
|
|
206
|
+
})]);
|
|
207
|
+
} catch (_e) {
|
|
208
|
+
// swallow: flush is best-effort; never throw into app code
|
|
209
|
+
} finally {
|
|
210
|
+
await this._crashBuffer.clear();
|
|
211
|
+
this._isFlushingBuffer = false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async _createExceptionSession(span) {
|
|
215
|
+
try {
|
|
216
|
+
const session = await this._apiService.createErrorSession({
|
|
217
|
+
span
|
|
218
|
+
});
|
|
219
|
+
if (session) {
|
|
220
|
+
void this._flushBuffer(session._id);
|
|
221
|
+
}
|
|
222
|
+
} catch (_ignored) {
|
|
223
|
+
// best-effort
|
|
224
|
+
}
|
|
225
|
+
}
|
|
134
226
|
async _loadStoredSessionData() {
|
|
135
227
|
try {
|
|
136
228
|
await StorageService.initialize();
|
|
@@ -170,22 +262,63 @@ class SessionRecorder extends Observable {
|
|
|
170
262
|
await this._loadStoredSessionData();
|
|
171
263
|
setMaxCapturingHttpPayloadSize(this._configs.maxCapturingHttpPayloadSize);
|
|
172
264
|
setShouldRecordHttpData(this._configs.captureBody, this._configs.captureHeaders);
|
|
265
|
+
|
|
266
|
+
// Crash buffer wiring (RN): set BEFORE tracer init so early spans don't get exported.
|
|
267
|
+
const bufferEnabled = Boolean(this._configs.buffering?.enabled);
|
|
268
|
+
const windowMs = Math.max(10_000, (this._configs.buffering?.windowMinutes || 2) * 60 * 1000);
|
|
269
|
+
this._tracer.setCrashBuffer(bufferEnabled ? this._crashBuffer : undefined, windowMs);
|
|
173
270
|
this._tracer.init(this._configs);
|
|
174
271
|
this._apiService.init(this._configs);
|
|
175
272
|
this._socketService.init({
|
|
176
273
|
apiKey: this._configs.apiKey,
|
|
177
274
|
socketUrl: this._configs.apiBaseUrl,
|
|
178
|
-
keepAlive: this._configs.useWebsocket
|
|
275
|
+
keepAlive: this._configs.useWebsocket,
|
|
276
|
+
clientId: this._tracer.clientId
|
|
277
|
+
});
|
|
278
|
+
this._recorder.init(this._configs, this._socketService, bufferEnabled ? this._crashBuffer : undefined, {
|
|
279
|
+
enabled: bufferEnabled,
|
|
280
|
+
windowMs
|
|
281
|
+
});
|
|
282
|
+
this._crashBuffer.on('error-span-appended', payload => {
|
|
283
|
+
if (this.sessionState !== SessionState.stopped || this.sessionId) return;
|
|
284
|
+
if (!payload.span) return;
|
|
285
|
+
this._createExceptionSession(payload.span);
|
|
179
286
|
});
|
|
180
|
-
this._recorder.init(this._configs, this._socketService);
|
|
181
287
|
await this._networkService.init();
|
|
182
288
|
this._setupNetworkCallbacks();
|
|
183
289
|
this._registerSocketServiceListeners();
|
|
184
290
|
if (this.sessionId && (this.sessionState === SessionState.started || this.sessionState === SessionState.paused)) {
|
|
185
291
|
this._start();
|
|
292
|
+
} else {
|
|
293
|
+
this._startBufferOnlyRecording();
|
|
186
294
|
}
|
|
187
295
|
this.emit('init', []);
|
|
188
296
|
}
|
|
297
|
+
_startBufferOnlyRecording() {
|
|
298
|
+
if (!this._configs?.buffering?.enabled) return;
|
|
299
|
+
if (this.sessionState !== SessionState.stopped || this.sessionId) return;
|
|
300
|
+
const windowMs = Math.max(10_000, (this._configs.buffering.windowMinutes || 2) * 60 * 1000);
|
|
301
|
+
|
|
302
|
+
// Best-effort: persist current attrs so flush has context.
|
|
303
|
+
this._crashBuffer.setAttrs({
|
|
304
|
+
sessionAttributes: this.sessionAttributes,
|
|
305
|
+
resourceAttributes: getNavigatorInfo(),
|
|
306
|
+
userAttributes: this._userAttributes
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Wire buffer into tracer + recorder (only used when sessionId is null).
|
|
310
|
+
this._tracer.setCrashBuffer(this._crashBuffer, windowMs);
|
|
311
|
+
this._recorder.init(this._configs, this._socketService, this._crashBuffer, {
|
|
312
|
+
enabled: true,
|
|
313
|
+
windowMs
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// Start capturing events without an active debug session id.
|
|
317
|
+
try {
|
|
318
|
+
this._recorder.stop();
|
|
319
|
+
} catch (_e) {}
|
|
320
|
+
this._recorder.start(null, SessionType.MANUAL);
|
|
321
|
+
}
|
|
189
322
|
|
|
190
323
|
/**
|
|
191
324
|
* Register socket service event listeners
|
|
@@ -207,6 +340,11 @@ class SessionRecorder extends Observable {
|
|
|
207
340
|
this.stop();
|
|
208
341
|
}
|
|
209
342
|
});
|
|
343
|
+
this._socketService.on(SESSION_SAVE_BUFFER_EVENT, payload => {
|
|
344
|
+
if (payload?.debugSession?._id) {
|
|
345
|
+
void this._flushBuffer(payload.debugSession._id);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
210
348
|
}
|
|
211
349
|
|
|
212
350
|
/**
|
|
@@ -437,6 +575,10 @@ class SessionRecorder extends Observable {
|
|
|
437
575
|
this.sessionState = SessionState.started;
|
|
438
576
|
this.sessionType = this.sessionType;
|
|
439
577
|
if (this.sessionId) {
|
|
578
|
+
// Switch from buffer-only recording to session recording cleanly.
|
|
579
|
+
try {
|
|
580
|
+
this._recorder.stop();
|
|
581
|
+
} catch (_e) {}
|
|
440
582
|
this._tracer.start(this.sessionId, this.sessionType);
|
|
441
583
|
this._recorder.start(this.sessionId, this.sessionType);
|
|
442
584
|
if (this.session) {
|
|
@@ -451,15 +593,16 @@ class SessionRecorder extends Observable {
|
|
|
451
593
|
_stop() {
|
|
452
594
|
this.sessionState = SessionState.stopped;
|
|
453
595
|
this._socketService.unsubscribeFromSession(true);
|
|
454
|
-
this._tracer.
|
|
596
|
+
this._tracer.stop();
|
|
455
597
|
this._recorder.stop();
|
|
598
|
+
this._startBufferOnlyRecording();
|
|
456
599
|
}
|
|
457
600
|
|
|
458
601
|
/**
|
|
459
602
|
* Pause the session tracing and recording
|
|
460
603
|
*/
|
|
461
604
|
_pause() {
|
|
462
|
-
this._tracer.
|
|
605
|
+
this._tracer.stop();
|
|
463
606
|
this._recorder.stop();
|
|
464
607
|
this.sessionState = SessionState.paused;
|
|
465
608
|
}
|
|
@@ -469,7 +612,10 @@ class SessionRecorder extends Observable {
|
|
|
469
612
|
*/
|
|
470
613
|
_resume() {
|
|
471
614
|
if (this.sessionId) {
|
|
472
|
-
this._tracer.
|
|
615
|
+
this._tracer.start(this.sessionId, this.sessionType);
|
|
616
|
+
try {
|
|
617
|
+
this._recorder.stop();
|
|
618
|
+
} catch (_e) {}
|
|
473
619
|
this._recorder.start(this.sessionId, this.sessionType);
|
|
474
620
|
}
|
|
475
621
|
this.sessionState = SessionState.started;
|