rollbar 2.26.3 → 3.0.0-alpha.1
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/.cursor/rules/guidelines.mdc +154 -0
- package/.github/workflows/ci.yml +32 -12
- package/.lgtm.yml +7 -7
- package/.prettierignore +18 -0
- package/.vscode/settings.json +39 -0
- package/CHANGELOG.md +121 -35
- package/CLAUDE.md +201 -0
- package/Gruntfile.js +101 -48
- package/Makefile +3 -3
- package/README.md +2 -4
- package/SECURITY.md +5 -0
- package/babel.config.json +9 -0
- package/bower.json +1 -3
- package/codex.md +148 -0
- package/defaults.js +17 -5
- package/dist/plugins/jquery.min.js +1 -1
- package/dist/rollbar.js +18748 -5375
- 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 +19368 -6000
- package/dist/rollbar.named-amd.js.map +1 -1
- package/dist/rollbar.named-amd.min.js +3 -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 +18749 -5380
- package/dist/rollbar.noconflict.umd.js.map +1 -1
- package/dist/rollbar.noconflict.umd.min.js +3 -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 +19367 -6000
- package/dist/rollbar.umd.js.map +1 -1
- package/dist/rollbar.umd.min.js +3 -1
- package/dist/rollbar.umd.min.js.LICENSE.txt +1 -0
- package/dist/rollbar.umd.min.js.map +1 -1
- package/docs/extension-exceptions.md +35 -30
- package/docs/migration_v0_to_v1.md +41 -38
- package/eslint.config.mjs +33 -0
- package/get_versions.js +33 -0
- package/index.d.ts +270 -231
- package/karma.conf.js +18 -27
- package/package.json +21 -21
- package/prettier.config.js +7 -0
- package/src/api.js +78 -14
- package/src/apiUtility.js +14 -11
- package/src/browser/core.js +138 -72
- package/src/browser/defaults/scrubFields.js +3 -3
- package/src/browser/detection.js +7 -8
- package/src/browser/domUtility.js +18 -8
- package/src/browser/globalSetup.js +12 -6
- package/src/browser/logger.js +1 -1
- package/src/browser/plugins/jquery.js +35 -35
- package/src/browser/predicates.js +1 -1
- package/src/browser/replay/defaults.js +71 -0
- package/src/browser/replay/recorder.js +193 -0
- package/src/browser/replay/replayMap.js +195 -0
- package/src/browser/rollbar.js +12 -8
- package/src/browser/rollbarWrapper.js +8 -5
- package/src/browser/shim.js +43 -19
- package/src/browser/snippet_callback.js +6 -4
- package/src/browser/telemetry.js +573 -361
- package/src/browser/transforms.js +46 -27
- package/src/browser/transport/fetch.js +26 -14
- package/src/browser/transport/xhr.js +41 -14
- package/src/browser/transport.js +93 -33
- package/src/browser/url.js +16 -8
- package/src/browser/wrapGlobals.js +27 -8
- package/src/defaults.js +3 -3
- package/src/errorParser.js +14 -11
- package/src/merge.js +32 -23
- package/src/notifier.js +16 -13
- package/src/predicates.js +43 -23
- package/src/queue.js +133 -40
- package/src/rateLimiter.js +59 -18
- package/src/react-native/logger.js +1 -1
- package/src/react-native/rollbar.js +59 -55
- package/src/react-native/transforms.js +13 -9
- package/src/react-native/transport.js +44 -34
- package/src/rollbar.js +72 -21
- package/src/scrub.js +0 -1
- package/src/server/locals.js +69 -39
- package/src/server/logger.js +4 -4
- package/src/server/parser.js +72 -47
- package/src/server/rollbar.js +135 -56
- package/src/server/sourceMap/stackTrace.js +33 -18
- package/src/server/telemetry/urlHelpers.js +9 -11
- package/src/server/telemetry.js +68 -45
- package/src/server/transforms.js +37 -21
- package/src/server/transport.js +62 -32
- package/src/telemetry.js +162 -33
- 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/transforms.js +33 -21
- package/src/truncation.js +8 -5
- package/src/utility/headers.js +43 -43
- package/src/utility/replace.js +9 -0
- package/src/utility/traverse.js +1 -1
- package/src/utility.js +123 -52
- package/test/api.test.js +88 -41
- package/test/apiUtility.test.js +48 -50
- package/test/browser.core.test.js +142 -141
- package/test/browser.domUtility.test.js +53 -36
- package/test/browser.predicates.test.js +14 -14
- package/test/browser.replay.recorder.test.js +416 -0
- package/test/browser.rollbar.test.js +655 -515
- package/test/browser.telemetry.test.js +46 -39
- package/test/browser.transforms.test.js +164 -139
- package/test/browser.transport.test.js +59 -50
- package/test/browser.url.test.js +13 -12
- package/test/fixtures/locals.fixtures.js +245 -126
- 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 +91 -79
- package/test/predicates.test.js +261 -215
- package/test/queue.test.js +231 -215
- package/test/rateLimiter.test.js +51 -43
- package/test/react-native.rollbar.test.js +150 -116
- package/test/react-native.transforms.test.js +23 -25
- package/test/react-native.transport.test.js +26 -14
- 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 +178 -38
- 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 +70 -68
- package/test/truncation.test.js +57 -55
- package/test/utility.test.js +310 -228
- package/webpack.config.js +36 -70
- package/.eslintignore +0 -7
- package/.gitmodules +0 -3
- package/test/server.lambda.test.js +0 -177
- package/test/server.locals.test.js +0 -841
- package/test/server.parser.test.js +0 -72
- package/test/server.predicates.test.js +0 -89
- package/test/server.rollbar.test.js +0 -676
- package/test/server.telemetry.test.js +0 -318
- package/test/server.transforms.test.js +0 -1099
- package/test/server.transport.test.js +0 -201
package/src/defaults.js
CHANGED
package/src/errorParser.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
var ErrorStackParser = require('error-stack-parser');
|
|
2
2
|
|
|
3
3
|
var UNKNOWN_FUNCTION = '?';
|
|
4
|
-
var ERR_CLASS_REGEXP = new RegExp(
|
|
4
|
+
var ERR_CLASS_REGEXP = new RegExp(
|
|
5
|
+
'^(([a-zA-Z0-9-_$ ]*): *)?(Uncaught )?([a-zA-Z0-9-_$ ]*): ',
|
|
6
|
+
);
|
|
5
7
|
|
|
6
8
|
function guessFunctionName() {
|
|
7
9
|
return UNKNOWN_FUNCTION;
|
|
8
10
|
}
|
|
9
11
|
|
|
10
|
-
|
|
11
12
|
function gatherContext() {
|
|
12
13
|
return null;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
|
|
16
16
|
function Frame(stackFrame) {
|
|
17
17
|
var data = {};
|
|
18
18
|
|
|
@@ -29,7 +29,6 @@ function Frame(stackFrame) {
|
|
|
29
29
|
return data;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
33
32
|
function Stack(exception, skip) {
|
|
34
33
|
function getStack() {
|
|
35
34
|
var parserStack = [];
|
|
@@ -38,7 +37,7 @@ function Stack(exception, skip) {
|
|
|
38
37
|
|
|
39
38
|
try {
|
|
40
39
|
parserStack = ErrorStackParser.parse(exception);
|
|
41
|
-
} catch(e) {
|
|
40
|
+
} catch (e) {
|
|
42
41
|
parserStack = [];
|
|
43
42
|
}
|
|
44
43
|
|
|
@@ -56,11 +55,10 @@ function Stack(exception, skip) {
|
|
|
56
55
|
message: exception.message,
|
|
57
56
|
name: _mostSpecificErrorName(exception),
|
|
58
57
|
rawStack: exception.stack,
|
|
59
|
-
rawException: exception
|
|
58
|
+
rawException: exception,
|
|
60
59
|
};
|
|
61
60
|
}
|
|
62
61
|
|
|
63
|
-
|
|
64
62
|
function parse(e, skip) {
|
|
65
63
|
var err = e;
|
|
66
64
|
|
|
@@ -81,7 +79,6 @@ function parse(e, skip) {
|
|
|
81
79
|
}
|
|
82
80
|
}
|
|
83
81
|
|
|
84
|
-
|
|
85
82
|
function guessErrorClass(errMsg) {
|
|
86
83
|
if (!errMsg || !errMsg.match) {
|
|
87
84
|
return ['Unknown error. There was no error message to display.', ''];
|
|
@@ -91,7 +88,10 @@ function guessErrorClass(errMsg) {
|
|
|
91
88
|
|
|
92
89
|
if (errClassMatch) {
|
|
93
90
|
errClass = errClassMatch[errClassMatch.length - 1];
|
|
94
|
-
errMsg = errMsg.replace(
|
|
91
|
+
errMsg = errMsg.replace(
|
|
92
|
+
(errClassMatch[errClassMatch.length - 2] || '') + errClass + ':',
|
|
93
|
+
'',
|
|
94
|
+
);
|
|
95
95
|
errMsg = errMsg.replace(/(^[\s]+|[\s]+$)/g, '');
|
|
96
96
|
}
|
|
97
97
|
return [errClass, errMsg];
|
|
@@ -102,7 +102,10 @@ function guessErrorClass(errMsg) {
|
|
|
102
102
|
// * Prefers name over constructor.name when both are more specific than 'Error'
|
|
103
103
|
function _mostSpecificErrorName(error) {
|
|
104
104
|
var name = error.name && error.name.length && error.name;
|
|
105
|
-
var constructorName =
|
|
105
|
+
var constructorName =
|
|
106
|
+
error.constructor.name &&
|
|
107
|
+
error.constructor.name.length &&
|
|
108
|
+
error.constructor.name;
|
|
106
109
|
|
|
107
110
|
if (!name || !constructorName) {
|
|
108
111
|
return name || constructorName;
|
|
@@ -120,5 +123,5 @@ module.exports = {
|
|
|
120
123
|
gatherContext: gatherContext,
|
|
121
124
|
parse: parse,
|
|
122
125
|
Stack: Stack,
|
|
123
|
-
Frame: Frame
|
|
126
|
+
Frame: Frame,
|
|
124
127
|
};
|
package/src/merge.js
CHANGED
|
@@ -4,32 +4,41 @@ var hasOwn = Object.prototype.hasOwnProperty;
|
|
|
4
4
|
var toStr = Object.prototype.toString;
|
|
5
5
|
|
|
6
6
|
var isPlainObject = function isPlainObject(obj) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
7
|
+
if (!obj || toStr.call(obj) !== '[object Object]') {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
var hasOwnConstructor = hasOwn.call(obj, 'constructor');
|
|
12
|
+
var hasIsPrototypeOf =
|
|
13
|
+
obj.constructor &&
|
|
14
|
+
obj.constructor.prototype &&
|
|
15
|
+
hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
|
|
16
|
+
// Not own constructor property must be Object
|
|
17
|
+
if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Own properties are enumerated firstly, so to speed up,
|
|
22
|
+
// if last one is own, then all properties are own.
|
|
23
|
+
var key;
|
|
24
|
+
for (key in obj) {
|
|
25
|
+
/**/
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return typeof key === 'undefined' || hasOwn.call(obj, key);
|
|
24
29
|
};
|
|
25
30
|
|
|
26
31
|
function merge() {
|
|
27
|
-
var i,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
var i,
|
|
33
|
+
src,
|
|
34
|
+
copy,
|
|
35
|
+
clone,
|
|
36
|
+
name,
|
|
37
|
+
result = {},
|
|
38
|
+
current = null,
|
|
39
|
+
length = arguments.length;
|
|
40
|
+
|
|
41
|
+
for (i = 0; i < length; i++) {
|
|
33
42
|
current = arguments[i];
|
|
34
43
|
if (current == null) {
|
|
35
44
|
continue;
|
package/src/notifier.js
CHANGED
|
@@ -22,7 +22,7 @@ function Notifier(queue, options) {
|
|
|
22
22
|
* @param options - an object which gets merged with the current options set on this notifier
|
|
23
23
|
* @returns this
|
|
24
24
|
*/
|
|
25
|
-
Notifier.prototype.configure = function(options) {
|
|
25
|
+
Notifier.prototype.configure = function (options) {
|
|
26
26
|
this.queue && this.queue.configure(options);
|
|
27
27
|
var oldOptions = this.options;
|
|
28
28
|
this.options = _.merge(oldOptions, options);
|
|
@@ -40,7 +40,7 @@ Notifier.prototype.configure = function(options) {
|
|
|
40
40
|
* with an error to terminate the processing. The item should be the updated item after this
|
|
41
41
|
* transform is finished modifying it.
|
|
42
42
|
*/
|
|
43
|
-
Notifier.prototype.addTransform = function(transform) {
|
|
43
|
+
Notifier.prototype.addTransform = function (transform) {
|
|
44
44
|
if (_.isFunction(transform)) {
|
|
45
45
|
this.transforms.push(transform);
|
|
46
46
|
}
|
|
@@ -60,9 +60,9 @@ Notifier.prototype.addTransform = function(transform) {
|
|
|
60
60
|
* transform stage if an error occurs inside a transform, or in response to the communication with
|
|
61
61
|
* the backend. The second argument will be the response from the backend in case of success.
|
|
62
62
|
*/
|
|
63
|
-
Notifier.prototype.log = function(item, callback) {
|
|
63
|
+
Notifier.prototype.log = function (item, callback) {
|
|
64
64
|
if (!callback || !_.isFunction(callback)) {
|
|
65
|
-
callback = function() {};
|
|
65
|
+
callback = function () {};
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
if (!this.options.enabled) {
|
|
@@ -71,13 +71,16 @@ Notifier.prototype.log = function(item, callback) {
|
|
|
71
71
|
|
|
72
72
|
this.queue.addPendingItem(item);
|
|
73
73
|
var originalError = item.err;
|
|
74
|
-
this._applyTransforms(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
this._applyTransforms(
|
|
75
|
+
item,
|
|
76
|
+
function (err, i) {
|
|
77
|
+
if (err) {
|
|
78
|
+
this.queue.removePendingItem(item);
|
|
79
|
+
return callback(err, null);
|
|
80
|
+
}
|
|
81
|
+
this.queue.addItem(i, callback, originalError, item);
|
|
82
|
+
}.bind(this),
|
|
83
|
+
);
|
|
81
84
|
};
|
|
82
85
|
|
|
83
86
|
/* Internal */
|
|
@@ -91,13 +94,13 @@ Notifier.prototype.log = function(item, callback) {
|
|
|
91
94
|
* error and a null item in the case of a transform failure, or a null error and non-null item after
|
|
92
95
|
* all transforms have been applied.
|
|
93
96
|
*/
|
|
94
|
-
Notifier.prototype._applyTransforms = function(item, callback) {
|
|
97
|
+
Notifier.prototype._applyTransforms = function (item, callback) {
|
|
95
98
|
var transformIndex = -1;
|
|
96
99
|
var transformsLength = this.transforms.length;
|
|
97
100
|
var transforms = this.transforms;
|
|
98
101
|
var options = this.options;
|
|
99
102
|
|
|
100
|
-
var cb = function(err, i) {
|
|
103
|
+
var cb = function (err, i) {
|
|
101
104
|
if (err) {
|
|
102
105
|
callback(err, null);
|
|
103
106
|
return;
|
package/src/predicates.js
CHANGED
|
@@ -13,7 +13,7 @@ function checkLevel(item, settings) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function userCheckIgnore(logger) {
|
|
16
|
-
return function(item, settings) {
|
|
16
|
+
return function (item, settings) {
|
|
17
17
|
var isUncaught = !!item._isUncaught;
|
|
18
18
|
delete item._isUncaught;
|
|
19
19
|
var args = item._originalArgs;
|
|
@@ -27,7 +27,10 @@ function userCheckIgnore(logger) {
|
|
|
27
27
|
logger.error('Error while calling onSendCallback, removing', e);
|
|
28
28
|
}
|
|
29
29
|
try {
|
|
30
|
-
if (
|
|
30
|
+
if (
|
|
31
|
+
_.isFunction(settings.checkIgnore) &&
|
|
32
|
+
settings.checkIgnore(isUncaught, args, item)
|
|
33
|
+
) {
|
|
31
34
|
return false;
|
|
32
35
|
}
|
|
33
36
|
} catch (e) {
|
|
@@ -35,27 +38,31 @@ function userCheckIgnore(logger) {
|
|
|
35
38
|
logger.error('Error while calling custom checkIgnore(), removing', e);
|
|
36
39
|
}
|
|
37
40
|
return true;
|
|
38
|
-
}
|
|
41
|
+
};
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
function urlIsNotBlockListed(logger) {
|
|
42
|
-
return function(item, settings) {
|
|
45
|
+
return function (item, settings) {
|
|
43
46
|
return !urlIsOnAList(item, settings, 'blocklist', logger);
|
|
44
|
-
}
|
|
47
|
+
};
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
function urlIsSafeListed(logger) {
|
|
48
|
-
return function(item, settings) {
|
|
51
|
+
return function (item, settings) {
|
|
49
52
|
return urlIsOnAList(item, settings, 'safelist', logger);
|
|
50
|
-
}
|
|
53
|
+
};
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
function matchFrames(trace, list, block) {
|
|
54
|
-
if (!trace) {
|
|
57
|
+
if (!trace) {
|
|
58
|
+
return !block;
|
|
59
|
+
}
|
|
55
60
|
|
|
56
61
|
var frames = trace.frames;
|
|
57
62
|
|
|
58
|
-
if (!frames || frames.length === 0) {
|
|
63
|
+
if (!frames || frames.length === 0) {
|
|
64
|
+
return !block;
|
|
65
|
+
}
|
|
59
66
|
|
|
60
67
|
var frame, filename, url, urlRegex;
|
|
61
68
|
var listLength = list.length;
|
|
@@ -64,7 +71,9 @@ function matchFrames(trace, list, block) {
|
|
|
64
71
|
frame = frames[i];
|
|
65
72
|
filename = frame.filename;
|
|
66
73
|
|
|
67
|
-
if (!_.isType(filename, 'string')) {
|
|
74
|
+
if (!_.isType(filename, 'string')) {
|
|
75
|
+
return !block;
|
|
76
|
+
}
|
|
68
77
|
|
|
69
78
|
for (var j = 0; j < listLength; j++) {
|
|
70
79
|
url = list[j];
|
|
@@ -101,27 +110,35 @@ function urlIsOnAList(item, settings, safeOrBlock, logger) {
|
|
|
101
110
|
|
|
102
111
|
var tracesLength = traces.length;
|
|
103
112
|
for (var i = 0; i < tracesLength; i++) {
|
|
104
|
-
if(matchFrames(traces[i], list, block)) {
|
|
113
|
+
if (matchFrames(traces[i], list, block)) {
|
|
105
114
|
return true;
|
|
106
115
|
}
|
|
107
116
|
}
|
|
108
|
-
} catch (
|
|
109
|
-
|
|
110
|
-
|
|
117
|
+
} catch (
|
|
118
|
+
e
|
|
119
|
+
/* istanbul ignore next */
|
|
120
|
+
) {
|
|
111
121
|
if (block) {
|
|
112
122
|
settings.hostBlockList = null;
|
|
113
123
|
} else {
|
|
114
124
|
settings.hostSafeList = null;
|
|
115
125
|
}
|
|
116
126
|
var listName = block ? 'hostBlockList' : 'hostSafeList';
|
|
117
|
-
logger.error(
|
|
127
|
+
logger.error(
|
|
128
|
+
"Error while reading your configuration's " +
|
|
129
|
+
listName +
|
|
130
|
+
' option. Removing custom ' +
|
|
131
|
+
listName +
|
|
132
|
+
'.',
|
|
133
|
+
e,
|
|
134
|
+
);
|
|
118
135
|
return !block;
|
|
119
136
|
}
|
|
120
137
|
return false;
|
|
121
138
|
}
|
|
122
139
|
|
|
123
140
|
function messageIsIgnored(logger) {
|
|
124
|
-
return function(item, settings) {
|
|
141
|
+
return function (item, settings) {
|
|
125
142
|
var i, j, ignoredMessages, len, messageIsIgnored, rIgnoredMessage, messages;
|
|
126
143
|
|
|
127
144
|
try {
|
|
@@ -134,7 +151,7 @@ function messageIsIgnored(logger) {
|
|
|
134
151
|
|
|
135
152
|
messages = messagesFromItem(item);
|
|
136
153
|
|
|
137
|
-
if (messages.length === 0){
|
|
154
|
+
if (messages.length === 0) {
|
|
138
155
|
return true;
|
|
139
156
|
}
|
|
140
157
|
|
|
@@ -150,15 +167,18 @@ function messageIsIgnored(logger) {
|
|
|
150
167
|
}
|
|
151
168
|
}
|
|
152
169
|
}
|
|
153
|
-
} catch(
|
|
154
|
-
|
|
155
|
-
|
|
170
|
+
} catch (
|
|
171
|
+
e
|
|
172
|
+
/* istanbul ignore next */
|
|
173
|
+
) {
|
|
156
174
|
settings.ignoredMessages = null;
|
|
157
|
-
logger.error(
|
|
175
|
+
logger.error(
|
|
176
|
+
"Error while reading your configuration's ignoredMessages option. Removing custom ignoredMessages.",
|
|
177
|
+
);
|
|
158
178
|
}
|
|
159
179
|
|
|
160
180
|
return true;
|
|
161
|
-
}
|
|
181
|
+
};
|
|
162
182
|
}
|
|
163
183
|
|
|
164
184
|
function messagesFromItem(item) {
|
|
@@ -189,5 +209,5 @@ module.exports = {
|
|
|
189
209
|
userCheckIgnore: userCheckIgnore,
|
|
190
210
|
urlIsNotBlockListed: urlIsNotBlockListed,
|
|
191
211
|
urlIsSafeListed: urlIsSafeListed,
|
|
192
|
-
messageIsIgnored: messageIsIgnored
|
|
212
|
+
messageIsIgnored: messageIsIgnored,
|
|
193
213
|
};
|
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 = [];
|
|
@@ -32,7 +34,7 @@ function Queue(rateLimiter, api, logger, options) {
|
|
|
32
34
|
*
|
|
33
35
|
* @param options
|
|
34
36
|
*/
|
|
35
|
-
Queue.prototype.configure = function(options) {
|
|
37
|
+
Queue.prototype.configure = function (options) {
|
|
36
38
|
this.api && this.api.configure(options);
|
|
37
39
|
var oldOptions = this.options;
|
|
38
40
|
this.options = _.merge(oldOptions, options);
|
|
@@ -48,18 +50,18 @@ Queue.prototype.configure = function(options) {
|
|
|
48
50
|
* Returning {err: Error} means do not add the item to the queue, and the given error explains why
|
|
49
51
|
* Returning {err: undefined} is equivalent to returning true but don't do that
|
|
50
52
|
*/
|
|
51
|
-
Queue.prototype.addPredicate = function(predicate) {
|
|
53
|
+
Queue.prototype.addPredicate = function (predicate) {
|
|
52
54
|
if (_.isFunction(predicate)) {
|
|
53
55
|
this.predicates.push(predicate);
|
|
54
56
|
}
|
|
55
57
|
return this;
|
|
56
58
|
};
|
|
57
59
|
|
|
58
|
-
Queue.prototype.addPendingItem = function(item) {
|
|
60
|
+
Queue.prototype.addPendingItem = function (item) {
|
|
59
61
|
this.pendingItems.push(item);
|
|
60
62
|
};
|
|
61
63
|
|
|
62
|
-
Queue.prototype.removePendingItem = function(item) {
|
|
64
|
+
Queue.prototype.removePendingItem = function (item) {
|
|
63
65
|
var idx = this.pendingItems.indexOf(item);
|
|
64
66
|
if (idx !== -1) {
|
|
65
67
|
this.pendingItems.splice(idx, 1);
|
|
@@ -76,9 +78,16 @@ Queue.prototype.removePendingItem = function(item) {
|
|
|
76
78
|
* to be an error condition, but nonetheless did not send the item to the API.
|
|
77
79
|
* @param originalError - The original error before any transformations that is to be logged if any
|
|
78
80
|
*/
|
|
79
|
-
Queue.prototype.addItem = function(
|
|
81
|
+
Queue.prototype.addItem = function (
|
|
82
|
+
item,
|
|
83
|
+
callback,
|
|
84
|
+
originalError,
|
|
85
|
+
originalItem,
|
|
86
|
+
) {
|
|
80
87
|
if (!callback || !_.isFunction(callback)) {
|
|
81
|
-
callback = function() {
|
|
88
|
+
callback = function () {
|
|
89
|
+
return;
|
|
90
|
+
};
|
|
82
91
|
}
|
|
83
92
|
var predicateResult = this._applyPredicates(item);
|
|
84
93
|
if (predicateResult.stop) {
|
|
@@ -92,12 +101,28 @@ Queue.prototype.addItem = function(item, callback, originalError, originalItem)
|
|
|
92
101
|
callback(new Error('Transmit disabled'));
|
|
93
102
|
return;
|
|
94
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
|
+
|
|
95
112
|
this.pendingRequests.push(item);
|
|
96
113
|
try {
|
|
97
|
-
this._makeApiRequest(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
114
|
+
this._makeApiRequest(
|
|
115
|
+
item,
|
|
116
|
+
function (err, resp, headers) {
|
|
117
|
+
this._dequeuePendingRequest(item);
|
|
118
|
+
|
|
119
|
+
if (!err && resp && item.replayId) {
|
|
120
|
+
this._handleReplayResponse(item.replayId, resp, headers);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
callback(err, resp);
|
|
124
|
+
}.bind(this),
|
|
125
|
+
);
|
|
101
126
|
} catch (e) {
|
|
102
127
|
this._dequeuePendingRequest(item);
|
|
103
128
|
callback(e);
|
|
@@ -110,7 +135,7 @@ Queue.prototype.addItem = function(item, callback, originalError, originalItem)
|
|
|
110
135
|
*
|
|
111
136
|
* @param callback - function() called when all pending items have been sent
|
|
112
137
|
*/
|
|
113
|
-
Queue.prototype.wait = function(callback) {
|
|
138
|
+
Queue.prototype.wait = function (callback) {
|
|
114
139
|
if (!_.isFunction(callback)) {
|
|
115
140
|
return;
|
|
116
141
|
}
|
|
@@ -121,9 +146,12 @@ Queue.prototype.wait = function(callback) {
|
|
|
121
146
|
if (this.waitIntervalID) {
|
|
122
147
|
this.waitIntervalID = clearInterval(this.waitIntervalID);
|
|
123
148
|
}
|
|
124
|
-
this.waitIntervalID = setInterval(
|
|
125
|
-
|
|
126
|
-
|
|
149
|
+
this.waitIntervalID = setInterval(
|
|
150
|
+
function () {
|
|
151
|
+
this._maybeCallWait();
|
|
152
|
+
}.bind(this),
|
|
153
|
+
500,
|
|
154
|
+
);
|
|
127
155
|
};
|
|
128
156
|
|
|
129
157
|
/* _applyPredicates - Sequentially applies the predicates that have been added to the queue to the
|
|
@@ -133,15 +161,15 @@ Queue.prototype.wait = function(callback) {
|
|
|
133
161
|
* @returns {stop: bool, err: (Error|null)} - stop being true means do not add item to the queue,
|
|
134
162
|
* the error value should be passed up to a callbak if we are stopping.
|
|
135
163
|
*/
|
|
136
|
-
Queue.prototype._applyPredicates = function(item) {
|
|
164
|
+
Queue.prototype._applyPredicates = function (item) {
|
|
137
165
|
var p = null;
|
|
138
166
|
for (var i = 0, len = this.predicates.length; i < len; i++) {
|
|
139
167
|
p = this.predicates[i](item, this.options);
|
|
140
168
|
if (!p || p.err !== undefined) {
|
|
141
|
-
return {stop: true, err: p.err};
|
|
169
|
+
return { stop: true, err: p.err };
|
|
142
170
|
}
|
|
143
171
|
}
|
|
144
|
-
return {stop: false, err: null};
|
|
172
|
+
return { stop: false, err: null };
|
|
145
173
|
};
|
|
146
174
|
|
|
147
175
|
/*
|
|
@@ -151,16 +179,19 @@ Queue.prototype._applyPredicates = function(item) {
|
|
|
151
179
|
* @param item - an item ready to send to the backend
|
|
152
180
|
* @param callback - function(err, response)
|
|
153
181
|
*/
|
|
154
|
-
Queue.prototype._makeApiRequest = function(item, callback) {
|
|
182
|
+
Queue.prototype._makeApiRequest = function (item, callback) {
|
|
155
183
|
var rateLimitResponse = this.rateLimiter.shouldSend(item);
|
|
156
184
|
if (rateLimitResponse.shouldSend) {
|
|
157
|
-
this.api.postItem(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
185
|
+
this.api.postItem(
|
|
186
|
+
item,
|
|
187
|
+
function (err, resp, headers) {
|
|
188
|
+
if (err) {
|
|
189
|
+
this._maybeRetry(err, item, callback);
|
|
190
|
+
} else {
|
|
191
|
+
callback(err, resp, headers);
|
|
192
|
+
}
|
|
193
|
+
}.bind(this),
|
|
194
|
+
);
|
|
164
195
|
} else if (rateLimitResponse.error) {
|
|
165
196
|
callback(rateLimitResponse.error);
|
|
166
197
|
} else {
|
|
@@ -169,7 +200,16 @@ Queue.prototype._makeApiRequest = function(item, callback) {
|
|
|
169
200
|
};
|
|
170
201
|
|
|
171
202
|
// These are errors basically mean there is no internet connection
|
|
172
|
-
var RETRIABLE_ERRORS = [
|
|
203
|
+
var RETRIABLE_ERRORS = [
|
|
204
|
+
'ECONNRESET',
|
|
205
|
+
'ENOTFOUND',
|
|
206
|
+
'ESOCKETTIMEDOUT',
|
|
207
|
+
'ETIMEDOUT',
|
|
208
|
+
'ECONNREFUSED',
|
|
209
|
+
'EHOSTUNREACH',
|
|
210
|
+
'EPIPE',
|
|
211
|
+
'EAI_AGAIN',
|
|
212
|
+
];
|
|
173
213
|
|
|
174
214
|
/*
|
|
175
215
|
* _maybeRetry - Given the error returned by the API, decide if we should retry or just callback
|
|
@@ -179,7 +219,7 @@ var RETRIABLE_ERRORS = ['ECONNRESET', 'ENOTFOUND', 'ESOCKETTIMEDOUT', 'ETIMEDOUT
|
|
|
179
219
|
* @param item - the item that was trying to be sent when this error occured
|
|
180
220
|
* @param callback - function(err, response)
|
|
181
221
|
*/
|
|
182
|
-
Queue.prototype._maybeRetry = function(err, item, callback) {
|
|
222
|
+
Queue.prototype._maybeRetry = function (err, item, callback) {
|
|
183
223
|
var shouldRetry = false;
|
|
184
224
|
if (this.options.retryInterval) {
|
|
185
225
|
for (var i = 0, len = RETRIABLE_ERRORS.length; i < len; i++) {
|
|
@@ -209,16 +249,19 @@ Queue.prototype._maybeRetry = function(err, item, callback) {
|
|
|
209
249
|
* @param item - an item that failed to send due to an error we deem retriable
|
|
210
250
|
* @param callback - function(err, response)
|
|
211
251
|
*/
|
|
212
|
-
Queue.prototype._retryApiRequest = function(item, callback) {
|
|
213
|
-
this.retryQueue.push({item: item, callback: callback});
|
|
252
|
+
Queue.prototype._retryApiRequest = function (item, callback) {
|
|
253
|
+
this.retryQueue.push({ item: item, callback: callback });
|
|
214
254
|
|
|
215
255
|
if (!this.retryHandle) {
|
|
216
|
-
this.retryHandle = setInterval(
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
256
|
+
this.retryHandle = setInterval(
|
|
257
|
+
function () {
|
|
258
|
+
while (this.retryQueue.length) {
|
|
259
|
+
var retryObject = this.retryQueue.shift();
|
|
260
|
+
this._makeApiRequest(retryObject.item, retryObject.callback);
|
|
261
|
+
}
|
|
262
|
+
}.bind(this),
|
|
263
|
+
this.options.retryInterval,
|
|
264
|
+
);
|
|
222
265
|
}
|
|
223
266
|
};
|
|
224
267
|
|
|
@@ -230,7 +273,7 @@ Queue.prototype._retryApiRequest = function(item, callback) {
|
|
|
230
273
|
*
|
|
231
274
|
* @param item - the item previously added to the pending request queue
|
|
232
275
|
*/
|
|
233
|
-
Queue.prototype._dequeuePendingRequest = function(item) {
|
|
276
|
+
Queue.prototype._dequeuePendingRequest = function (item) {
|
|
234
277
|
var idx = this.pendingRequests.indexOf(item);
|
|
235
278
|
if (idx !== -1) {
|
|
236
279
|
this.pendingRequests.splice(idx, 1);
|
|
@@ -238,7 +281,7 @@ Queue.prototype._dequeuePendingRequest = function(item) {
|
|
|
238
281
|
}
|
|
239
282
|
};
|
|
240
283
|
|
|
241
|
-
Queue.prototype._maybeLog = function(data, originalError) {
|
|
284
|
+
Queue.prototype._maybeLog = function (data, originalError) {
|
|
242
285
|
if (this.logger && this.options.verbose) {
|
|
243
286
|
var message = originalError;
|
|
244
287
|
message = message || _.get(data, 'body.trace.exception.message');
|
|
@@ -254,8 +297,12 @@ Queue.prototype._maybeLog = function(data, originalError) {
|
|
|
254
297
|
}
|
|
255
298
|
};
|
|
256
299
|
|
|
257
|
-
Queue.prototype._maybeCallWait = function() {
|
|
258
|
-
if (
|
|
300
|
+
Queue.prototype._maybeCallWait = function () {
|
|
301
|
+
if (
|
|
302
|
+
_.isFunction(this.waitCallback) &&
|
|
303
|
+
this.pendingItems.length === 0 &&
|
|
304
|
+
this.pendingRequests.length === 0
|
|
305
|
+
) {
|
|
259
306
|
if (this.waitIntervalID) {
|
|
260
307
|
this.waitIntervalID = clearInterval(this.waitIntervalID);
|
|
261
308
|
}
|
|
@@ -265,4 +312,50 @@ Queue.prototype._maybeCallWait = function() {
|
|
|
265
312
|
return false;
|
|
266
313
|
};
|
|
267
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
|
+
|
|
268
361
|
module.exports = Queue;
|