rollbar 2.26.4 → 3.0.0-alpha.2
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/.claude/settings.local.json +3 -0
- package/.cursor/rules/guidelines.mdc +154 -0
- package/.github/workflows/ci.yml +4 -6
- package/CLAUDE.local.md +297 -0
- package/CLAUDE.md +201 -0
- package/CLAUDE.testrunner.md +470 -0
- package/Gruntfile.js +59 -16
- package/Makefile +3 -3
- package/SECURITY.md +5 -0
- package/babel.config.json +9 -0
- package/codex.md +148 -0
- package/dist/plugins/jquery.min.js +1 -1
- package/dist/rollbar.js +19332 -6596
- package/dist/rollbar.js.map +1 -1
- package/dist/rollbar.min.js +2 -1
- package/dist/rollbar.min.js.LICENSE.txt +1 -0
- package/dist/rollbar.min.js.map +1 -1
- package/dist/rollbar.named-amd.js +19332 -6596
- package/dist/rollbar.named-amd.js.map +1 -1
- package/dist/rollbar.named-amd.min.js +2 -1
- package/dist/rollbar.named-amd.min.js.LICENSE.txt +1 -0
- package/dist/rollbar.named-amd.min.js.map +1 -1
- package/dist/rollbar.noconflict.umd.js +19319 -6581
- package/dist/rollbar.noconflict.umd.js.map +1 -1
- package/dist/rollbar.noconflict.umd.min.js +2 -1
- package/dist/rollbar.noconflict.umd.min.js.LICENSE.txt +1 -0
- package/dist/rollbar.noconflict.umd.min.js.map +1 -1
- package/dist/rollbar.snippet.js +1 -1
- package/dist/rollbar.umd.js +19333 -6597
- package/dist/rollbar.umd.js.map +1 -1
- package/dist/rollbar.umd.min.js +2 -1
- package/dist/rollbar.umd.min.js.LICENSE.txt +1 -0
- package/dist/rollbar.umd.min.js.map +1 -1
- package/eslint.config.mjs +33 -0
- package/karma.conf.js +5 -14
- package/package.json +19 -20
- package/src/api.js +57 -4
- package/src/apiUtility.js +2 -3
- package/src/browser/core.js +37 -9
- package/src/browser/replay/defaults.js +70 -0
- package/src/browser/replay/recorder.js +194 -0
- package/src/browser/replay/replayMap.js +195 -0
- package/src/browser/rollbar.js +11 -7
- package/src/browser/telemetry.js +3 -3
- package/src/browser/transport/fetch.js +17 -4
- package/src/browser/transport/xhr.js +17 -1
- package/src/browser/transport.js +11 -8
- package/src/defaults.js +1 -1
- package/src/queue.js +65 -4
- package/src/react-native/rollbar.js +1 -1
- package/src/rollbar.js +52 -10
- package/src/server/rollbar.js +3 -2
- package/src/telemetry.js +76 -11
- package/src/tracing/context.js +24 -0
- package/src/tracing/contextManager.js +37 -0
- package/src/tracing/defaults.js +7 -0
- package/src/tracing/exporter.js +188 -0
- package/src/tracing/hrtime.js +98 -0
- package/src/tracing/id.js +24 -0
- package/src/tracing/session.js +55 -0
- package/src/tracing/span.js +92 -0
- package/src/tracing/spanProcessor.js +15 -0
- package/src/tracing/tracer.js +46 -0
- package/src/tracing/tracing.js +89 -0
- package/src/utility.js +34 -0
- package/test/api.test.js +57 -12
- package/test/apiUtility.test.js +5 -6
- package/test/browser.core.test.js +1 -10
- package/test/browser.domUtility.test.js +1 -1
- package/test/browser.predicates.test.js +1 -1
- package/test/browser.replay.recorder.test.js +430 -0
- package/test/browser.rollbar.test.js +58 -12
- package/test/browser.telemetry.test.js +1 -1
- package/test/browser.transforms.test.js +20 -13
- package/test/browser.transport.test.js +5 -4
- package/test/browser.url.test.js +1 -1
- package/test/fixtures/replay/index.js +20 -0
- package/test/fixtures/replay/payloads.fixtures.js +229 -0
- package/test/fixtures/replay/rrwebEvents.fixtures.js +251 -0
- package/test/fixtures/replay/rrwebSyntheticEvents.fixtures.js +328 -0
- package/test/notifier.test.js +1 -1
- package/test/predicates.test.js +1 -1
- package/test/queue.test.js +1 -1
- package/test/rateLimiter.test.js +1 -1
- package/test/react-native.rollbar.test.js +1 -1
- package/test/react-native.transforms.test.js +2 -2
- package/test/react-native.transport.test.js +3 -3
- package/test/replay/index.js +2 -0
- package/test/replay/integration/api.spans.test.js +136 -0
- package/test/replay/integration/e2e.test.js +228 -0
- package/test/replay/integration/index.js +9 -0
- package/test/replay/integration/queue.replayMap.test.js +332 -0
- package/test/replay/integration/replayMap.test.js +163 -0
- package/test/replay/integration/sessionRecording.test.js +390 -0
- package/test/replay/unit/api.postSpans.test.js +150 -0
- package/test/replay/unit/index.js +7 -0
- package/test/replay/unit/queue.replayMap.test.js +225 -0
- package/test/replay/unit/replayMap.test.js +348 -0
- package/test/replay/util/index.js +5 -0
- package/test/replay/util/mockRecordFn.js +80 -0
- package/test/server.lambda.mocha.test.mjs +172 -0
- package/test/server.locals.constructor.mocha.test.mjs +80 -0
- package/test/server.locals.error-handling.mocha.test.mjs +387 -0
- package/test/server.locals.merge.mocha.test.mjs +267 -0
- package/test/server.locals.test-utils.mjs +114 -0
- package/test/server.parser.mocha.test.mjs +87 -0
- package/test/server.predicates.mocha.test.mjs +63 -0
- package/test/server.rollbar.constructor.mocha.test.mjs +199 -0
- package/test/server.rollbar.handlers.mocha.test.mjs +253 -0
- package/test/server.rollbar.logging.mocha.test.mjs +326 -0
- package/test/server.rollbar.misc.mocha.test.mjs +44 -0
- package/test/server.rollbar.test-utils.mjs +57 -0
- package/test/server.telemetry.mocha.test.mjs +377 -0
- package/test/server.transforms.data.mocha.test.mjs +163 -0
- package/test/server.transforms.error.mocha.test.mjs +199 -0
- package/test/server.transforms.request.mocha.test.mjs +208 -0
- package/test/server.transforms.scrub.mocha.test.mjs +140 -0
- package/test/server.transforms.sourcemaps.mocha.test.mjs +122 -0
- package/test/server.transforms.test-utils.mjs +62 -0
- package/test/server.transport.mocha.test.mjs +269 -0
- package/test/telemetry.test.js +132 -1
- package/test/tracing/contextManager.test.js +28 -0
- package/test/tracing/exporter.toPayload.test.js +400 -0
- package/test/tracing/id.test.js +24 -0
- package/test/tracing/span.test.js +183 -0
- package/test/tracing/spanProcessor.test.js +73 -0
- package/test/tracing/tracing.test.js +105 -0
- package/test/transforms.test.js +2 -2
- package/test/truncation.test.js +2 -2
- package/test/utility.test.js +44 -6
- package/webpack.config.js +6 -44
- package/.eslintignore +0 -7
- package/test/server.lambda.test.js +0 -194
- package/test/server.locals.test.js +0 -1068
- package/test/server.parser.test.js +0 -78
- package/test/server.predicates.test.js +0 -91
- package/test/server.rollbar.test.js +0 -728
- package/test/server.telemetry.test.js +0 -443
- package/test/server.transforms.test.js +0 -1193
- package/test/server.transport.test.js +0 -269
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import id from '../../tracing/id.js';
|
|
2
|
+
import logger from '../logger.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ReplayMap - Manages the mapping between error occurrences and their associated
|
|
6
|
+
* session recordings. This class handles the coordination between when recordings
|
|
7
|
+
* are dumped and when they are eventually sent to the backend.
|
|
8
|
+
*/
|
|
9
|
+
export default class ReplayMap {
|
|
10
|
+
#map;
|
|
11
|
+
#recorder;
|
|
12
|
+
#api;
|
|
13
|
+
#tracing;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new ReplayMap instance
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} props - Configuration props
|
|
19
|
+
* @param {Object} props.recorder - The recorder instance that dumps replay data into spans
|
|
20
|
+
* @param {Object} props.api - The API instance used to send replay payloads to the backend
|
|
21
|
+
* @param {Object} props.tracing - The tracing instance used to create spans and manage context
|
|
22
|
+
*/
|
|
23
|
+
constructor({ recorder, api, tracing }) {
|
|
24
|
+
if (!recorder) {
|
|
25
|
+
throw new TypeError("Expected 'recorder' to be provided");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!api) {
|
|
29
|
+
throw new TypeError("Expected 'api' to be provided");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!tracing) {
|
|
33
|
+
throw new TypeError("Expected 'tracing' to be provided");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.#map = new Map();
|
|
37
|
+
this.#recorder = recorder;
|
|
38
|
+
this.#api = api;
|
|
39
|
+
this.#tracing = tracing;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Processes a replay by converting recorder events into a transport-ready payload.
|
|
44
|
+
*
|
|
45
|
+
* Calls recorder.dump() to capture events as spans, formats them into a proper payload,
|
|
46
|
+
* and stores the result in the map using replayId as the key.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} replayId - The unique ID for this replay
|
|
49
|
+
* @returns {Promise<string>} A promise resolving to the processed replayId
|
|
50
|
+
* @private
|
|
51
|
+
*/
|
|
52
|
+
async _processReplay(replayId, occurrenceUuid) {
|
|
53
|
+
try {
|
|
54
|
+
const payload = this.#recorder.dump(
|
|
55
|
+
this.#tracing,
|
|
56
|
+
replayId,
|
|
57
|
+
occurrenceUuid,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
this.#map.set(replayId, payload);
|
|
61
|
+
} catch (transformError) {
|
|
62
|
+
logger.error('Error transforming spans:', transformError);
|
|
63
|
+
|
|
64
|
+
this.#map.set(replayId, null); // TODO(matux): Error span?
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return replayId;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Adds a replay to the map and returns a uniquely generated replay ID.
|
|
72
|
+
*
|
|
73
|
+
* This method immediately returns the replayId and asynchronously processes
|
|
74
|
+
* the replay data in the background. The processing involves converting
|
|
75
|
+
* recorder events into a payload format and storing it in the map.
|
|
76
|
+
*
|
|
77
|
+
* @returns {string} A unique identifier for this replay
|
|
78
|
+
*/
|
|
79
|
+
add(replayId, occurrenceUuid) {
|
|
80
|
+
replayId = replayId || id.gen(8);
|
|
81
|
+
|
|
82
|
+
this._processReplay(replayId, occurrenceUuid).catch((error) => {
|
|
83
|
+
logger.error('Failed to process replay:', error);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return replayId;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Sends the replay payload associated with the given replayId to the backend
|
|
91
|
+
* and removes it from the map.
|
|
92
|
+
*
|
|
93
|
+
* Retrieves the payload from the map, checks if it's valid, then sends it
|
|
94
|
+
* to the API endpoint for processing. The payload can be either a spans array
|
|
95
|
+
* or a formatted OTLP payload object.
|
|
96
|
+
*
|
|
97
|
+
* @param {string} replayId - The ID of the replay to send
|
|
98
|
+
* @returns {Promise<boolean>} A promise that resolves to true if the payload was found and sent, false otherwise
|
|
99
|
+
*/
|
|
100
|
+
async send(replayId) {
|
|
101
|
+
if (!replayId) {
|
|
102
|
+
logger.error('ReplayMap.send: No replayId provided');
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!this.#map.has(replayId)) {
|
|
107
|
+
logger.error(`ReplayMap.send: No replay found for replayId: ${replayId}`);
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const payload = this.#map.get(replayId);
|
|
112
|
+
this.#map.delete(replayId);
|
|
113
|
+
|
|
114
|
+
// Check if payload is empty (could be raw spans array or OTLP payload)
|
|
115
|
+
const isEmpty =
|
|
116
|
+
!payload ||
|
|
117
|
+
(Array.isArray(payload) && payload.length === 0) ||
|
|
118
|
+
(payload.resourceSpans && payload.resourceSpans.length === 0);
|
|
119
|
+
|
|
120
|
+
if (isEmpty) {
|
|
121
|
+
logger.error(
|
|
122
|
+
`ReplayMap.send: No payload found for replayId: ${replayId}`,
|
|
123
|
+
);
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
await this.#api.postSpans(payload);
|
|
129
|
+
return true;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
logger.error('Error sending replay:', error);
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Discards the replay associated with the given replay ID by removing
|
|
138
|
+
* it from the map without sending it.
|
|
139
|
+
*
|
|
140
|
+
* @param {string} replayId - The ID of the replay to discard
|
|
141
|
+
* @returns {boolean} True if a replay was found and discarded, false otherwise
|
|
142
|
+
*/
|
|
143
|
+
discard(replayId) {
|
|
144
|
+
if (!replayId) {
|
|
145
|
+
logger.error('ReplayMap.discard: No replayId provided');
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (!this.#map.has(replayId)) {
|
|
150
|
+
logger.error(
|
|
151
|
+
`ReplayMap.discard: No replay found for replayId: ${replayId}`,
|
|
152
|
+
);
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this.#map.delete(replayId);
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Gets spans for the given replay ID
|
|
162
|
+
*
|
|
163
|
+
* @param {string} replayId - The ID to retrieve spans for
|
|
164
|
+
* @returns {Array|null} The spans array or null if not found
|
|
165
|
+
*/
|
|
166
|
+
getSpans(replayId) {
|
|
167
|
+
return this.#map.get(replayId) ?? null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Sets spans for a given replay ID
|
|
172
|
+
*
|
|
173
|
+
* @param {string} replayId - The ID to set spans for
|
|
174
|
+
* @param {Array} spans - The spans to set
|
|
175
|
+
*/
|
|
176
|
+
setSpans(replayId, spans) {
|
|
177
|
+
this.#map.set(replayId, spans);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Returns the size of the map (number of stored replays)
|
|
182
|
+
*
|
|
183
|
+
* @returns {number} The number of replays currently stored
|
|
184
|
+
*/
|
|
185
|
+
get size() {
|
|
186
|
+
return this.#map.size;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Clears all stored replays without sending them
|
|
191
|
+
*/
|
|
192
|
+
clear() {
|
|
193
|
+
this.#map.clear();
|
|
194
|
+
}
|
|
195
|
+
}
|
package/src/browser/rollbar.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
const Rollbar = require('./core');
|
|
2
|
+
const telemeter = require('../telemetry');
|
|
3
|
+
const instrumenter = require('./telemetry');
|
|
4
|
+
const polyfillJSON = require('../utility/polyfillJSON');
|
|
5
|
+
const wrapGlobals = require('./wrapGlobals');
|
|
6
|
+
const scrub = require('../scrub');
|
|
7
|
+
const truncation = require('../truncation');
|
|
8
|
+
const Tracing = require('../tracing/tracing');
|
|
9
|
+
const Recorder = require('./replay/recorder');
|
|
8
10
|
|
|
9
11
|
Rollbar.setComponents({
|
|
10
12
|
telemeter: telemeter,
|
|
@@ -13,6 +15,8 @@ Rollbar.setComponents({
|
|
|
13
15
|
wrapGlobals: wrapGlobals,
|
|
14
16
|
scrub: scrub,
|
|
15
17
|
truncation: truncation,
|
|
18
|
+
tracing: Tracing.default,
|
|
19
|
+
recorder: Recorder.default,
|
|
16
20
|
});
|
|
17
21
|
|
|
18
22
|
module.exports = Rollbar;
|
package/src/browser/telemetry.js
CHANGED
|
@@ -613,7 +613,7 @@ Instrumenter.prototype.instrumentConsole = function () {
|
|
|
613
613
|
c[method] = function () {
|
|
614
614
|
var args = Array.prototype.slice.call(arguments);
|
|
615
615
|
var message = _.formatArgsAsString(args);
|
|
616
|
-
self.telemeter.captureLog(message, level);
|
|
616
|
+
self.telemeter.captureLog(message, level, null, _.now());
|
|
617
617
|
if (orig) {
|
|
618
618
|
Function.prototype.apply.call(orig, origConsole, args);
|
|
619
619
|
}
|
|
@@ -822,7 +822,7 @@ Instrumenter.prototype.handleUrlChange = function (from, to) {
|
|
|
822
822
|
) {
|
|
823
823
|
from = parsedFrom.path + (parsedFrom.hash || '');
|
|
824
824
|
}
|
|
825
|
-
this.telemeter.captureNavigation(from, to);
|
|
825
|
+
this.telemeter.captureNavigation(from, to, null, _.now());
|
|
826
826
|
};
|
|
827
827
|
|
|
828
828
|
Instrumenter.prototype.deinstrumentConnectivity = function () {
|
|
@@ -922,7 +922,7 @@ Instrumenter.prototype.handleCspEvent = function (cspEvent) {
|
|
|
922
922
|
|
|
923
923
|
message += 'originalPolicy: ' + cspEvent.originalPolicy;
|
|
924
924
|
|
|
925
|
-
this.telemeter.captureLog(message, 'error');
|
|
925
|
+
this.telemeter.captureLog(message, 'error', null, _.now());
|
|
926
926
|
this.handleCspError(message);
|
|
927
927
|
};
|
|
928
928
|
|
|
@@ -23,10 +23,23 @@ function makeFetchRequest(accessToken, url, method, data, callback, timeout) {
|
|
|
23
23
|
})
|
|
24
24
|
.then(function (response) {
|
|
25
25
|
if (timeoutId) clearTimeout(timeoutId);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
const respHeaders = response.headers;
|
|
27
|
+
|
|
28
|
+
const isItemRoute = url.endsWith('/api/1/item/');
|
|
29
|
+
const headers = isItemRoute
|
|
30
|
+
? {
|
|
31
|
+
'Rollbar-Replay-Enabled': respHeaders.get('Rollbar-Replay-Enabled'),
|
|
32
|
+
'Rollbar-Replay-RateLimit-Remaining': respHeaders.get(
|
|
33
|
+
'Rollbar-Replay-RateLimit-Remaining',
|
|
34
|
+
),
|
|
35
|
+
'Rollbar-Replay-RateLimit-Reset': respHeaders.get(
|
|
36
|
+
'Rollbar-Replay-RateLimit-Reset',
|
|
37
|
+
),
|
|
38
|
+
}
|
|
39
|
+
: {};
|
|
40
|
+
|
|
41
|
+
const json = response.json();
|
|
42
|
+
callback(null, json, headers);
|
|
30
43
|
})
|
|
31
44
|
.catch(function (error) {
|
|
32
45
|
logger.error(error.message);
|
|
@@ -31,7 +31,23 @@ function makeXhrRequest(
|
|
|
31
31
|
|
|
32
32
|
var parseResponse = _.jsonParse(request.responseText);
|
|
33
33
|
if (_isSuccess(request)) {
|
|
34
|
-
|
|
34
|
+
const isItemRoute = url.endsWith('/api/1/item/');
|
|
35
|
+
|
|
36
|
+
const headers = isItemRoute
|
|
37
|
+
? {
|
|
38
|
+
'Rollbar-Replay-Enabled': request.getResponseHeader(
|
|
39
|
+
'Rollbar-Replay-Enabled',
|
|
40
|
+
),
|
|
41
|
+
'Rollbar-Replay-RateLimit-Remaining':
|
|
42
|
+
request.getResponseHeader(
|
|
43
|
+
'Rollbar-Replay-RateLimit-Remaining',
|
|
44
|
+
),
|
|
45
|
+
'Rollbar-Replay-RateLimit-Reset': request.getResponseHeader(
|
|
46
|
+
'Rollbar-Replay-RateLimit-Reset',
|
|
47
|
+
),
|
|
48
|
+
}
|
|
49
|
+
: {};
|
|
50
|
+
callback(parseResponse.error, parseResponse.value, headers);
|
|
35
51
|
return;
|
|
36
52
|
} else if (_isNormalFailure(request)) {
|
|
37
53
|
if (request.status === 403) {
|
package/src/browser/transport.js
CHANGED
|
@@ -66,7 +66,8 @@ Transport.prototype.post = function (
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
var stringifyResult;
|
|
69
|
-
|
|
69
|
+
// Check payload.body to ensure only items are truncated.
|
|
70
|
+
if (this.truncation && payload.body) {
|
|
70
71
|
stringifyResult = this.truncation.truncate(payload);
|
|
71
72
|
} else {
|
|
72
73
|
stringifyResult = _.stringify(payload);
|
|
@@ -115,19 +116,21 @@ Transport.prototype.postJsonPayload = function (
|
|
|
115
116
|
);
|
|
116
117
|
};
|
|
117
118
|
|
|
118
|
-
// Wraps _makeRequest
|
|
119
|
-
//
|
|
120
|
-
// This is
|
|
121
|
-
//
|
|
119
|
+
// Wraps `_makeRequest` if zone.js is being used, ensuring that Rollbar
|
|
120
|
+
// API calls are not intercepted by any child forked zones.
|
|
121
|
+
// This is equivalent to `NgZone.runOutsideAngular` in Angular.
|
|
122
122
|
Transport.prototype._makeZoneRequest = function () {
|
|
123
123
|
var gWindow =
|
|
124
124
|
(typeof window != 'undefined' && window) ||
|
|
125
125
|
(typeof self != 'undefined' && self);
|
|
126
|
-
|
|
126
|
+
// Whenever zone.js is loaded and `Zone` is exposed globally, access
|
|
127
|
+
// the root zone to ensure that requests are always made within it.
|
|
128
|
+
// This approach is framework-agnostic, regardless of which
|
|
129
|
+
// framework zone.js is used with.
|
|
130
|
+
var rootZone = gWindow && gWindow.Zone && gWindow.Zone.root;
|
|
127
131
|
var args = Array.prototype.slice.call(arguments);
|
|
128
132
|
|
|
129
|
-
if (
|
|
130
|
-
var rootZone = currentZone._parent;
|
|
133
|
+
if (rootZone) {
|
|
131
134
|
var self = this;
|
|
132
135
|
rootZone.run(function () {
|
|
133
136
|
self._makeRequest.apply(undefined, args);
|
package/src/defaults.js
CHANGED
package/src/queue.js
CHANGED
|
@@ -12,12 +12,14 @@ var _ = require('./utility');
|
|
|
12
12
|
* api.postItem(payload, function(err, response))
|
|
13
13
|
* @param logger - An object used to log verbose messages if desired
|
|
14
14
|
* @param options - see Queue.prototype.configure
|
|
15
|
+
* @param replayMap - Optional ReplayMap for coordinating session replay with error occurrences
|
|
15
16
|
*/
|
|
16
|
-
function Queue(rateLimiter, api, logger, options) {
|
|
17
|
+
function Queue(rateLimiter, api, logger, options, replayMap) {
|
|
17
18
|
this.rateLimiter = rateLimiter;
|
|
18
19
|
this.api = api;
|
|
19
20
|
this.logger = logger;
|
|
20
21
|
this.options = options;
|
|
22
|
+
this.replayMap = replayMap;
|
|
21
23
|
this.predicates = [];
|
|
22
24
|
this.pendingItems = [];
|
|
23
25
|
this.pendingRequests = [];
|
|
@@ -99,12 +101,25 @@ Queue.prototype.addItem = function (
|
|
|
99
101
|
callback(new Error('Transmit disabled'));
|
|
100
102
|
return;
|
|
101
103
|
}
|
|
104
|
+
|
|
105
|
+
if (this.replayMap && item.body) {
|
|
106
|
+
const replayId = _.getItemAttribute(item, 'replay_id');
|
|
107
|
+
if (replayId) {
|
|
108
|
+
item.replayId = this.replayMap.add(replayId, item.uuid);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
102
112
|
this.pendingRequests.push(item);
|
|
103
113
|
try {
|
|
104
114
|
this._makeApiRequest(
|
|
105
115
|
item,
|
|
106
|
-
function (err, resp) {
|
|
116
|
+
function (err, resp, headers) {
|
|
107
117
|
this._dequeuePendingRequest(item);
|
|
118
|
+
|
|
119
|
+
if (!err && resp && item.replayId) {
|
|
120
|
+
this._handleReplayResponse(item.replayId, resp, headers);
|
|
121
|
+
}
|
|
122
|
+
|
|
108
123
|
callback(err, resp);
|
|
109
124
|
}.bind(this),
|
|
110
125
|
);
|
|
@@ -169,11 +184,11 @@ Queue.prototype._makeApiRequest = function (item, callback) {
|
|
|
169
184
|
if (rateLimitResponse.shouldSend) {
|
|
170
185
|
this.api.postItem(
|
|
171
186
|
item,
|
|
172
|
-
function (err, resp) {
|
|
187
|
+
function (err, resp, headers) {
|
|
173
188
|
if (err) {
|
|
174
189
|
this._maybeRetry(err, item, callback);
|
|
175
190
|
} else {
|
|
176
|
-
callback(err, resp);
|
|
191
|
+
callback(err, resp, headers);
|
|
177
192
|
}
|
|
178
193
|
}.bind(this),
|
|
179
194
|
);
|
|
@@ -297,4 +312,50 @@ Queue.prototype._maybeCallWait = function () {
|
|
|
297
312
|
return false;
|
|
298
313
|
};
|
|
299
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Handles the API response for an item with a replay ID.
|
|
317
|
+
* Based on the success or failure status of the response,
|
|
318
|
+
* it either sends or discards the associated session replay.
|
|
319
|
+
*
|
|
320
|
+
* @param {string} replayId - The ID of the replay to handle
|
|
321
|
+
* @param {Object} response - The API response
|
|
322
|
+
* @returns {Promise<boolean>} A promise that resolves to true if replay was sent successfully,
|
|
323
|
+
* false if replay was discarded or an error occurred
|
|
324
|
+
* @private
|
|
325
|
+
*/
|
|
326
|
+
Queue.prototype._handleReplayResponse = async function (replayId, response, headers) {
|
|
327
|
+
if (!this.replayMap) {
|
|
328
|
+
console.warn('Queue._handleReplayResponse: ReplayMap not available');
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (!replayId) {
|
|
333
|
+
console.warn('Queue._handleReplayResponse: No replayId provided');
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
try {
|
|
338
|
+
if (this._shouldSendReplay(response, headers)) {
|
|
339
|
+
return await this.replayMap.send(replayId);
|
|
340
|
+
} else {
|
|
341
|
+
this.replayMap.discard(replayId);
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
} catch (error) {
|
|
345
|
+
console.error('Error handling replay response:', error);
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
Queue.prototype._shouldSendReplay = function (response, headers) {
|
|
351
|
+
if (response?.err !== 0 ||
|
|
352
|
+
!headers ||
|
|
353
|
+
headers['Rollbar-Replay-Enabled'] !== 'true' ||
|
|
354
|
+
headers['Rollbar-Replay-RateLimit-Remaining'] === '0') {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return true;
|
|
359
|
+
};
|
|
360
|
+
|
|
300
361
|
module.exports = Queue;
|
|
@@ -30,7 +30,7 @@ function Rollbar(options, client) {
|
|
|
30
30
|
var api = new API(this.options, transport, urllib, truncation);
|
|
31
31
|
var telemeter = new Telemeter(this.options);
|
|
32
32
|
this.client =
|
|
33
|
-
client || new Client(this.options, api, logger, telemeter, 'react-native');
|
|
33
|
+
client || new Client(this.options, api, logger, telemeter, null, null, 'react-native');
|
|
34
34
|
addTransformsToNotifier(this.client.notifier);
|
|
35
35
|
addPredicatesToQueue(this.client.queue);
|
|
36
36
|
_.setupJSON(polyfillJSON);
|
package/src/rollbar.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const RateLimiter = require('./rateLimiter');
|
|
2
|
+
const Queue = require('./queue');
|
|
3
|
+
const Notifier = require('./notifier');
|
|
4
|
+
const _ = require('./utility');
|
|
5
5
|
|
|
6
6
|
/*
|
|
7
7
|
* Rollbar - the interface to Rollbar
|
|
@@ -10,14 +10,17 @@ var _ = require('./utility');
|
|
|
10
10
|
* @param api
|
|
11
11
|
* @param logger
|
|
12
12
|
*/
|
|
13
|
-
function Rollbar(options, api, logger, telemeter, platform) {
|
|
13
|
+
function Rollbar(options, api, logger, telemeter, tracing, replayMap, platform) {
|
|
14
14
|
this.options = _.merge(options);
|
|
15
15
|
this.logger = logger;
|
|
16
16
|
Rollbar.rateLimiter.configureGlobal(this.options);
|
|
17
17
|
Rollbar.rateLimiter.setPlatformOptions(platform, this.options);
|
|
18
18
|
this.api = api;
|
|
19
|
-
this.queue = new Queue(Rollbar.rateLimiter, api, logger, this.options);
|
|
19
|
+
this.queue = new Queue(Rollbar.rateLimiter, api, logger, this.options, replayMap);
|
|
20
20
|
|
|
21
|
+
this.tracing = tracing;
|
|
22
|
+
|
|
23
|
+
// Legacy OpenTracing support
|
|
21
24
|
// This must happen before the Notifier is created
|
|
22
25
|
var tracer = this.options.tracer || null;
|
|
23
26
|
if (validateTracer(tracer)) {
|
|
@@ -57,6 +60,7 @@ Rollbar.prototype.configure = function (options, payloadData) {
|
|
|
57
60
|
|
|
58
61
|
this.options = _.merge(oldOptions, options, payload);
|
|
59
62
|
|
|
63
|
+
// Legacy OpenTracing support
|
|
60
64
|
// This must happen before the Notifier is configured
|
|
61
65
|
var tracer = this.options.tracer || null;
|
|
62
66
|
if (validateTracer(tracer)) {
|
|
@@ -150,11 +154,25 @@ Rollbar.prototype._log = function (defaultLevel, item) {
|
|
|
150
154
|
return;
|
|
151
155
|
}
|
|
152
156
|
try {
|
|
153
|
-
this._addTracingInfo(item);
|
|
154
157
|
item.level = item.level || defaultLevel;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
+
|
|
159
|
+
const replayId = this._replayIdIfTriggered(item);
|
|
160
|
+
this._addTracingAttributes(item, replayId);
|
|
161
|
+
|
|
162
|
+
// Legacy OpenTracing support
|
|
163
|
+
this._addTracingInfo(item);
|
|
164
|
+
|
|
165
|
+
const telemeter = this.telemeter;
|
|
166
|
+
if (telemeter) {
|
|
167
|
+
telemeter._captureRollbarItem(item);
|
|
168
|
+
item.telemetryEvents = telemeter.copyEvents() || [];
|
|
169
|
+
|
|
170
|
+
if (telemeter.telemetrySpan) {
|
|
171
|
+
telemeter.telemetrySpan.end({'rollbar.replay.id': replayId});
|
|
172
|
+
telemeter.telemetrySpan = telemeter.tracing.startSpan('rollbar-telemetry', {});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
158
176
|
this.notifier.log(item, callback);
|
|
159
177
|
} catch (e) {
|
|
160
178
|
if (callback) {
|
|
@@ -164,6 +182,30 @@ Rollbar.prototype._log = function (defaultLevel, item) {
|
|
|
164
182
|
}
|
|
165
183
|
};
|
|
166
184
|
|
|
185
|
+
Rollbar.prototype._addTracingAttributes = function (item, replayId) {
|
|
186
|
+
const span = this.tracing?.getSpan();
|
|
187
|
+
|
|
188
|
+
const attributes = [
|
|
189
|
+
{key: 'replay_id', value: replayId},
|
|
190
|
+
{key: 'session_id', value: this.tracing?.sessionId},
|
|
191
|
+
{key: 'span_id', value: span?.spanId},
|
|
192
|
+
{key: 'trace_id', value: span?.traceId},
|
|
193
|
+
];
|
|
194
|
+
_.addItemAttributes(item.data, attributes);
|
|
195
|
+
|
|
196
|
+
span?.addEvent(
|
|
197
|
+
'rollbar.occurrence',
|
|
198
|
+
[{key: 'rollbar.occurrence.uuid', value: item.uuid}],
|
|
199
|
+
);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
Rollbar.prototype._replayIdIfTriggered = function (item) {
|
|
203
|
+
const levels = this.options.recorder?.triggerOptions?.item?.levels || [];
|
|
204
|
+
if (levels.includes(item.level)) {
|
|
205
|
+
return this.tracing?.idGen(8);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
167
209
|
Rollbar.prototype._defaultLogLevel = function () {
|
|
168
210
|
return this.options.logLevel || 'debug';
|
|
169
211
|
};
|
package/src/server/rollbar.js
CHANGED
|
@@ -41,7 +41,7 @@ function Rollbar(options, client) {
|
|
|
41
41
|
var api = new API(this.options, transport, urllib, truncation, jsonBackup);
|
|
42
42
|
var telemeter = new Telemeter(this.options);
|
|
43
43
|
this.client =
|
|
44
|
-
client || new Client(this.options, api, logger, telemeter, 'server');
|
|
44
|
+
client || new Client(this.options, api, logger, telemeter, null, null, 'server');
|
|
45
45
|
this.instrumenter = new Instrumenter(
|
|
46
46
|
this.options,
|
|
47
47
|
this.client.telemeter,
|
|
@@ -250,8 +250,9 @@ Rollbar.error = function () {
|
|
|
250
250
|
Rollbar.prototype._uncaughtError = function () {
|
|
251
251
|
var item = this._createItem(arguments);
|
|
252
252
|
item._isUncaught = true;
|
|
253
|
+
item.level = this.options.uncaughtErrorLevel;
|
|
253
254
|
var uuid = item.uuid;
|
|
254
|
-
this.client.
|
|
255
|
+
this.client.log(item);
|
|
255
256
|
return { uuid: uuid };
|
|
256
257
|
};
|
|
257
258
|
|