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,33 @@
|
|
|
1
|
+
import babelParser from '@babel/eslint-parser';
|
|
2
|
+
import { defineConfig } from 'eslint/config';
|
|
3
|
+
|
|
4
|
+
export default defineConfig([
|
|
5
|
+
{
|
|
6
|
+
files: ['**/*.js', '**/*.mjs'],
|
|
7
|
+
languageOptions: {
|
|
8
|
+
ecmaVersion: 2021,
|
|
9
|
+
sourceType: 'module',
|
|
10
|
+
parser: babelParser,
|
|
11
|
+
parserOptions: {
|
|
12
|
+
ecmaVersion: 2021,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
rules: {
|
|
16
|
+
'comma-dangle': 'off',
|
|
17
|
+
'strict': 0,
|
|
18
|
+
'no-underscore-dangle': 0,
|
|
19
|
+
'no-useless-escape': 0,
|
|
20
|
+
'complexity': [2, { 'max': 35 }],
|
|
21
|
+
'no-use-before-define': [0, { 'functions': false }],
|
|
22
|
+
'no-prototype-builtins': 0,
|
|
23
|
+
|
|
24
|
+
// Off until issues are fixed
|
|
25
|
+
'camelcase': 'off', //[2, {'properties': 'never'}],
|
|
26
|
+
'no-unused-vars': 'off', //[2, { 'argsIgnorePattern': '^_' }],
|
|
27
|
+
'quotes': 'off', //[2, 'single', 'avoid-escape'],
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
ignores: ['dist', 'examples', 'node_modules', 'vendor'],
|
|
32
|
+
}
|
|
33
|
+
]);
|
package/karma.conf.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const webpack = require('webpack');
|
|
3
|
+
const defaults = require('./defaults');
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
const defaultsPlugin = new webpack.DefinePlugin(defaults);
|
|
6
6
|
|
|
7
7
|
module.exports = function (config) {
|
|
8
8
|
config.set({
|
|
@@ -66,17 +66,8 @@ module.exports = function (config) {
|
|
|
66
66
|
module: {
|
|
67
67
|
rules: [
|
|
68
68
|
{
|
|
69
|
-
enforce: 'pre',
|
|
70
69
|
test: /\.js$/,
|
|
71
|
-
loader: '
|
|
72
|
-
exclude: [/node_modules/, /vendor/, /lib/, /dist/],
|
|
73
|
-
options: {
|
|
74
|
-
configFile: path.resolve(__dirname, '.eslintrc'),
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
test: /\.js$/,
|
|
79
|
-
loader: 'strict-loader',
|
|
70
|
+
loader: 'babel-loader',
|
|
80
71
|
exclude: [/node_modules/, /vendor/, /lib/, /dist/, /test/],
|
|
81
72
|
},
|
|
82
73
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rollbar",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-alpha.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "http://github.com/rollbar/rollbar.js"
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"browser": "dist/rollbar.umd.min.js",
|
|
19
19
|
"types": "./index.d.ts",
|
|
20
20
|
"dependencies": {
|
|
21
|
+
"@rrweb/record": "^2.0.0-alpha.18",
|
|
21
22
|
"async": "~3.2.3",
|
|
22
23
|
"console-polyfill": "0.3.0",
|
|
23
24
|
"error-stack-parser": "^2.0.4",
|
|
@@ -27,20 +28,21 @@
|
|
|
27
28
|
"source-map": "^0.5.7"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
|
-
"@babel/core": "^7.
|
|
31
|
+
"@babel/core": "^7.26.10",
|
|
32
|
+
"@babel/eslint-parser": "^7.27.0",
|
|
33
|
+
"@babel/preset-env": "^7.26.9",
|
|
31
34
|
"babel-eslint": "^10.0.3",
|
|
32
|
-
"babel-loader": "^
|
|
35
|
+
"babel-loader": "^9.2.1",
|
|
33
36
|
"bluebird": "^3.3.5",
|
|
34
37
|
"chai": "^4.2.0",
|
|
35
38
|
"chalk": "^1.1.1",
|
|
36
39
|
"coverage-istanbul-loader": "^3.0.5",
|
|
37
|
-
"eslint": "^
|
|
38
|
-
"
|
|
39
|
-
"express": "^4.18.2",
|
|
40
|
+
"eslint": "^9.24.0",
|
|
41
|
+
"express": "^4.21.2",
|
|
40
42
|
"glob": "^5.0.14",
|
|
41
43
|
"grunt": "^1.1.0",
|
|
42
44
|
"grunt-bumpup": "^0.6.3",
|
|
43
|
-
"grunt-cli": "^1.
|
|
45
|
+
"grunt-cli": "^1.5.0",
|
|
44
46
|
"grunt-contrib-concat": "^2.1.0",
|
|
45
47
|
"grunt-contrib-connect": "^2.1.0",
|
|
46
48
|
"grunt-contrib-copy": "^1.0.0",
|
|
@@ -50,12 +52,11 @@
|
|
|
50
52
|
"grunt-karma": "^4.0.2",
|
|
51
53
|
"grunt-parallel": "^0.5.1",
|
|
52
54
|
"grunt-text-replace": "^0.4.0",
|
|
53
|
-
"grunt-vows": "^0.4.2",
|
|
54
55
|
"grunt-webpack": "^5.0.0",
|
|
55
56
|
"jade": "~0.27.7",
|
|
56
57
|
"jasmine-core": "^2.3.4",
|
|
57
58
|
"jquery-mockjax": "^2.5.0",
|
|
58
|
-
"karma": "^6.4.
|
|
59
|
+
"karma": "^6.4.4",
|
|
59
60
|
"karma-chai": "^0.1.0",
|
|
60
61
|
"karma-chrome-launcher": "^2.2.0",
|
|
61
62
|
"karma-expect": "^1.1.0",
|
|
@@ -69,30 +70,28 @@
|
|
|
69
70
|
"karma-sinon": "^1.0.4",
|
|
70
71
|
"karma-sourcemap-loader": "^0.3.5",
|
|
71
72
|
"karma-webpack": "^5.0.0",
|
|
72
|
-
"mocha": "^
|
|
73
|
+
"mocha": "^11.1.0",
|
|
73
74
|
"natives": "^1.1.6",
|
|
74
75
|
"nock": "^11.9.1",
|
|
75
76
|
"node-libs-browser": "^0.5.2",
|
|
76
77
|
"prettier": "^3.2.5",
|
|
77
|
-
"requirejs": "^2.
|
|
78
|
+
"requirejs": "^2.3.7",
|
|
78
79
|
"script-loader": "0.6.1",
|
|
79
80
|
"sinon": "^8.1.1",
|
|
80
81
|
"stackframe": "^0.2.2",
|
|
81
|
-
"strict-loader": "^1.2.0",
|
|
82
82
|
"time-grunt": "^1.0.0",
|
|
83
|
-
"
|
|
84
|
-
"webpack": "^5.88.2"
|
|
85
|
-
},
|
|
86
|
-
"optionalDependencies": {
|
|
87
|
-
"decache": "^3.0.5"
|
|
83
|
+
"webpack": "^5.98.0"
|
|
88
84
|
},
|
|
89
85
|
"scripts": {
|
|
90
86
|
"build": "./node_modules/.bin/grunt",
|
|
91
87
|
"test": "./node_modules/.bin/grunt test",
|
|
92
88
|
"test-browser": "./node_modules/.bin/grunt test-browser",
|
|
93
|
-
"test-server": "
|
|
94
|
-
"
|
|
95
|
-
"
|
|
89
|
+
"test-server": "mocha 'test/server.*.mocha.test.mjs' --reporter spec",
|
|
90
|
+
"test-replay": "./node_modules/.bin/grunt test-replay",
|
|
91
|
+
"test-replay-unit": "./node_modules/.bin/grunt test-replay-unit",
|
|
92
|
+
"test-replay-integration": "./node_modules/.bin/grunt test-replay-integration",
|
|
93
|
+
"test-ci": "./node_modules/.bin/grunt test && npm run test-server",
|
|
94
|
+
"lint": "./node_modules/.bin/eslint ."
|
|
96
95
|
},
|
|
97
96
|
"cdn": {
|
|
98
97
|
"host": "cdn.rollbar.com"
|
package/src/api.js
CHANGED
|
@@ -10,6 +10,15 @@ var defaultOptions = {
|
|
|
10
10
|
port: 443,
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
var OTLPDefaultOptions = {
|
|
14
|
+
hostname: 'api.rollbar.com',
|
|
15
|
+
path: '/api/1/session/',
|
|
16
|
+
search: null,
|
|
17
|
+
version: '1',
|
|
18
|
+
protocol: 'https:',
|
|
19
|
+
port: 443,
|
|
20
|
+
};
|
|
21
|
+
|
|
13
22
|
/**
|
|
14
23
|
* Api is an object that encapsulates methods of communicating with
|
|
15
24
|
* the Rollbar API. It is a standard interface with some parts implemented
|
|
@@ -29,16 +38,35 @@ var defaultOptions = {
|
|
|
29
38
|
* protocol (optional): https
|
|
30
39
|
* }
|
|
31
40
|
*/
|
|
32
|
-
function Api(options, transport, urllib, truncation
|
|
41
|
+
function Api(options, transport, urllib, truncation) {
|
|
33
42
|
this.options = options;
|
|
34
43
|
this.transport = transport;
|
|
35
44
|
this.url = urllib;
|
|
36
45
|
this.truncation = truncation;
|
|
37
|
-
this.jsonBackup = jsonBackup;
|
|
38
46
|
this.accessToken = options.accessToken;
|
|
39
47
|
this.transportOptions = _getTransport(options, urllib);
|
|
48
|
+
this.OTLPTransportOptions = _getOTLPTransport(options, urllib);
|
|
40
49
|
}
|
|
41
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Wraps transport.post in a Promise to support async/await
|
|
53
|
+
*
|
|
54
|
+
* @param {Object} options - Options for the API request
|
|
55
|
+
* @param {string} options.accessToken - The access token for authentication
|
|
56
|
+
* @param {Object} options.transportOptions - Options for the transport
|
|
57
|
+
* @param {Object} options.payload - The data payload to send
|
|
58
|
+
* @returns {Promise} A promise that resolves with the response or rejects with an error
|
|
59
|
+
* @private
|
|
60
|
+
*/
|
|
61
|
+
Api.prototype._postPromise = function({ accessToken, transportOptions, payload }) {
|
|
62
|
+
const self = this;
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
self.transport.post(accessToken, transportOptions, payload, (err, resp) =>
|
|
65
|
+
err ? reject(err) : resolve(resp)
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
42
70
|
/**
|
|
43
71
|
*
|
|
44
72
|
* @param data
|
|
@@ -49,7 +77,7 @@ Api.prototype.postItem = function (data, callback) {
|
|
|
49
77
|
this.transportOptions,
|
|
50
78
|
'POST',
|
|
51
79
|
);
|
|
52
|
-
var payload = helpers.buildPayload(
|
|
80
|
+
var payload = helpers.buildPayload(data);
|
|
53
81
|
var self = this;
|
|
54
82
|
|
|
55
83
|
// ensure the network request is scheduled after the current tick.
|
|
@@ -58,13 +86,32 @@ Api.prototype.postItem = function (data, callback) {
|
|
|
58
86
|
}, 0);
|
|
59
87
|
};
|
|
60
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Posts spans to the Rollbar API using the session endpoint
|
|
91
|
+
*
|
|
92
|
+
* @param {Array} payload - The spans to send
|
|
93
|
+
* @returns {Promise<Object>} A promise that resolves with the API response
|
|
94
|
+
*/
|
|
95
|
+
Api.prototype.postSpans = async function (payload) {
|
|
96
|
+
const transportOptions = helpers.transportOptions(
|
|
97
|
+
this.OTLPTransportOptions,
|
|
98
|
+
'POST',
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
return await this._postPromise({
|
|
102
|
+
accessToken: this.accessToken,
|
|
103
|
+
transportOptions,
|
|
104
|
+
payload
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
|
|
61
108
|
/**
|
|
62
109
|
*
|
|
63
110
|
* @param data
|
|
64
111
|
* @param callback
|
|
65
112
|
*/
|
|
66
113
|
Api.prototype.buildJsonPayload = function (data, callback) {
|
|
67
|
-
var payload = helpers.buildPayload(
|
|
114
|
+
var payload = helpers.buildPayload(data);
|
|
68
115
|
|
|
69
116
|
var stringifyResult;
|
|
70
117
|
if (this.truncation) {
|
|
@@ -105,6 +152,7 @@ Api.prototype.configure = function (options) {
|
|
|
105
152
|
var oldOptions = this.oldOptions;
|
|
106
153
|
this.options = _.merge(oldOptions, options);
|
|
107
154
|
this.transportOptions = _getTransport(this.options, this.url);
|
|
155
|
+
this.OTLPTransportOptions = _getOTLPTransport(this.options, this.url);
|
|
108
156
|
if (this.options.accessToken !== undefined) {
|
|
109
157
|
this.accessToken = this.options.accessToken;
|
|
110
158
|
}
|
|
@@ -115,4 +163,9 @@ function _getTransport(options, url) {
|
|
|
115
163
|
return helpers.getTransportFromOptions(options, defaultOptions, url);
|
|
116
164
|
}
|
|
117
165
|
|
|
166
|
+
function _getOTLPTransport(options, url) {
|
|
167
|
+
options = {...options, endpoint: options.tracing?.endpoint};
|
|
168
|
+
return helpers.getTransportFromOptions(options, OTLPDefaultOptions, url);
|
|
169
|
+
}
|
|
170
|
+
|
|
118
171
|
module.exports = Api;
|
package/src/apiUtility.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
var _ = require('./utility');
|
|
2
2
|
|
|
3
|
-
function buildPayload(
|
|
3
|
+
function buildPayload(data) {
|
|
4
4
|
if (!_.isType(data.context, 'string')) {
|
|
5
|
-
var contextResult = _.stringify(data.context
|
|
5
|
+
var contextResult = _.stringify(data.context);
|
|
6
6
|
if (contextResult.error) {
|
|
7
7
|
data.context = "Error: could not serialize 'context'";
|
|
8
8
|
} else {
|
|
@@ -13,7 +13,6 @@ function buildPayload(accessToken, data, jsonBackup) {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
return {
|
|
16
|
-
access_token: accessToken,
|
|
17
16
|
data: data,
|
|
18
17
|
};
|
|
19
18
|
}
|
package/src/browser/core.js
CHANGED
|
@@ -12,24 +12,48 @@ var sharedTransforms = require('../transforms');
|
|
|
12
12
|
var predicates = require('./predicates');
|
|
13
13
|
var sharedPredicates = require('../predicates');
|
|
14
14
|
var errorParser = require('../errorParser');
|
|
15
|
+
const recorderDefaults = require('./replay/defaults').default;
|
|
16
|
+
const tracingDefaults = require('../tracing/defaults').default;
|
|
17
|
+
const ReplayMap = require('./replay/replayMap').default;
|
|
15
18
|
|
|
16
19
|
function Rollbar(options, client) {
|
|
17
20
|
this.options = _.handleOptions(defaultOptions, options, null, logger);
|
|
18
21
|
this.options._configuredOptions = options;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
const Telemeter = this.components.telemeter;
|
|
23
|
+
const Instrumenter = this.components.instrumenter;
|
|
24
|
+
const polyfillJSON = this.components.polyfillJSON;
|
|
22
25
|
this.wrapGlobals = this.components.wrapGlobals;
|
|
23
26
|
this.scrub = this.components.scrub;
|
|
24
|
-
|
|
27
|
+
const truncation = this.components.truncation;
|
|
28
|
+
const Tracing = this.components.tracing;
|
|
29
|
+
const Recorder = this.components.recorder;
|
|
30
|
+
|
|
31
|
+
const transport = new Transport(truncation);
|
|
32
|
+
const api = new API(this.options, transport, urllib, truncation);
|
|
33
|
+
if (Tracing) {
|
|
34
|
+
this.tracing = new Tracing(_gWindow(), this.options);
|
|
35
|
+
this.tracing.initSession();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (Recorder && _.isBrowser()) {
|
|
39
|
+
const recorderOptions = this.options.recorder;
|
|
40
|
+
this.recorder = new Recorder(recorderOptions);
|
|
41
|
+
this.replayMap = new ReplayMap({
|
|
42
|
+
recorder: this.recorder,
|
|
43
|
+
api: api,
|
|
44
|
+
tracing: this.tracing
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (recorderOptions.enabled && recorderOptions.autoStart) {
|
|
48
|
+
this.recorder.start();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
25
51
|
|
|
26
|
-
var transport = new Transport(truncation);
|
|
27
|
-
var api = new API(this.options, transport, urllib, truncation);
|
|
28
52
|
if (Telemeter) {
|
|
29
|
-
this.telemeter = new Telemeter(this.options);
|
|
53
|
+
this.telemeter = new Telemeter(this.options, this.tracing);
|
|
30
54
|
}
|
|
31
55
|
this.client =
|
|
32
|
-
client || new Client(this.options, api, logger, this.telemeter, 'browser');
|
|
56
|
+
client || new Client(this.options, api, logger, this.telemeter, this.tracing, this.replayMap, 'browser');
|
|
33
57
|
var gWindow = _gWindow();
|
|
34
58
|
var gDocument = typeof document != 'undefined' && document;
|
|
35
59
|
this.isChrome = gWindow.chrome && gWindow.chrome.runtime; // check .runtime to avoid Edge browsers
|
|
@@ -94,12 +118,15 @@ Rollbar.prototype.configure = function (options, payloadData) {
|
|
|
94
118
|
if (payloadData) {
|
|
95
119
|
payload = { payload: payloadData };
|
|
96
120
|
}
|
|
121
|
+
|
|
97
122
|
this.options = _.handleOptions(oldOptions, options, payload, logger);
|
|
98
123
|
this.options._configuredOptions = _.handleOptions(
|
|
99
124
|
oldOptions._configuredOptions,
|
|
100
125
|
options,
|
|
101
126
|
payload,
|
|
102
127
|
);
|
|
128
|
+
|
|
129
|
+
this.recorder?.configure(this.options);
|
|
103
130
|
this.client.configure(this.options, payloadData);
|
|
104
131
|
this.instrumenter && this.instrumenter.configure(this.options);
|
|
105
132
|
this.setupUnhandledCapture();
|
|
@@ -344,7 +371,6 @@ Rollbar.prototype.handleAnonymousErrors = function () {
|
|
|
344
371
|
|
|
345
372
|
var r = this;
|
|
346
373
|
function prepareStackTrace(error, _stack) {
|
|
347
|
-
// eslint-disable-line no-unused-vars
|
|
348
374
|
if (r.options.inspectAnonymousErrors) {
|
|
349
375
|
if (r.anonymousErrorsPending) {
|
|
350
376
|
// This is the only known way to detect that onerror saw an anonymous error.
|
|
@@ -600,6 +626,8 @@ var defaultOptions = {
|
|
|
600
626
|
inspectAnonymousErrors: true,
|
|
601
627
|
ignoreDuplicateErrors: true,
|
|
602
628
|
wrapGlobalEventHandlers: false,
|
|
629
|
+
recorder: recorderDefaults,
|
|
630
|
+
tracing: tracingDefaults,
|
|
603
631
|
};
|
|
604
632
|
|
|
605
633
|
module.exports = Rollbar;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default options for the rrweb recorder
|
|
3
|
+
* See https://github.com/rrweb-io/rrweb/blob/master/guide.md#options for details
|
|
4
|
+
*/
|
|
5
|
+
export default {
|
|
6
|
+
enabled: false, // Whether recording is enabled
|
|
7
|
+
autoStart: true, // Start recording automatically when Rollbar initializes
|
|
8
|
+
maxSeconds: 300, // Maximum recording duration in seconds
|
|
9
|
+
|
|
10
|
+
triggerOptions: {
|
|
11
|
+
// Trigger replay on specific items (occurrences)
|
|
12
|
+
item: {
|
|
13
|
+
levels: ['error', 'critical'], // Trigger on item level
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
debug: {
|
|
18
|
+
logErrors: true, // Whether to log errors emitted by rrweb.
|
|
19
|
+
logEmits: false, // Whether to log emitted events
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
// Recording options
|
|
23
|
+
inlineStylesheet: true, // Whether to inline stylesheets to improve replay accuracy
|
|
24
|
+
inlineImages: false, // Whether to record the image content
|
|
25
|
+
collectFonts: true, // Whether to collect fonts in the website
|
|
26
|
+
|
|
27
|
+
// Privacy options
|
|
28
|
+
// Fine-grained control over which input types to mask
|
|
29
|
+
// By default only password inputs are masked if maskInputs is true
|
|
30
|
+
maskInputOptions: {
|
|
31
|
+
password: true,
|
|
32
|
+
email: false,
|
|
33
|
+
tel: false,
|
|
34
|
+
text: false,
|
|
35
|
+
color: false,
|
|
36
|
+
date: false,
|
|
37
|
+
'datetime-local': false,
|
|
38
|
+
month: false,
|
|
39
|
+
number: false,
|
|
40
|
+
range: false,
|
|
41
|
+
search: false,
|
|
42
|
+
time: false,
|
|
43
|
+
url: false,
|
|
44
|
+
week: false,
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// Remove unnecessary parts of the DOM
|
|
48
|
+
// By default all removable elements are removed
|
|
49
|
+
slimDOMOptions: {
|
|
50
|
+
script: true, // Remove script elements
|
|
51
|
+
comment: true, // Remove comments
|
|
52
|
+
headFavicon: true, // Remove favicons in the head
|
|
53
|
+
headWhitespace: true, // Remove whitespace in head
|
|
54
|
+
headMetaDescKeywords: true, // Remove meta description and keywords
|
|
55
|
+
headMetaSocial: true, // Remove social media meta tags
|
|
56
|
+
headMetaRobots: true, // Remove robots meta directives
|
|
57
|
+
headMetaHttpEquiv: true, // Remove http-equiv meta directives
|
|
58
|
+
headMetaAuthorship: true, // Remove authorship meta directives
|
|
59
|
+
headMetaVerification: true, // Remove verification meta directives
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// Custom callbacks for advanced use cases
|
|
63
|
+
// These are undefined by default and can be set programmatically
|
|
64
|
+
// maskInputFn: undefined, // Custom function to mask input values
|
|
65
|
+
// maskTextFn: undefined, // Custom function to mask text content
|
|
66
|
+
// errorHandler: undefined, // Custom error handler for recording errors
|
|
67
|
+
|
|
68
|
+
// Plugin system
|
|
69
|
+
// plugins: [] // List of plugins to use (must be set programmatically)
|
|
70
|
+
};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { record as rrwebRecordFn } from '@rrweb/record';
|
|
2
|
+
import { EventType } from '@rrweb/types';
|
|
3
|
+
|
|
4
|
+
import hrtime from '../../tracing/hrtime.js';
|
|
5
|
+
import logger from '../logger.js';
|
|
6
|
+
|
|
7
|
+
export default class Recorder {
|
|
8
|
+
#options;
|
|
9
|
+
#rrwebOptions;
|
|
10
|
+
#stopFn = null;
|
|
11
|
+
#recordFn;
|
|
12
|
+
#events = {
|
|
13
|
+
previous: [],
|
|
14
|
+
current: [],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new Recorder instance for capturing DOM events
|
|
19
|
+
*
|
|
20
|
+
* @param {Object} options - Configuration options for the recorder
|
|
21
|
+
* @param {Function} [recordFn=rrwebRecordFn] - The recording function to use
|
|
22
|
+
*/
|
|
23
|
+
constructor(options, recordFn = rrwebRecordFn) {
|
|
24
|
+
if (!recordFn) {
|
|
25
|
+
throw new TypeError("Expected 'recordFn' to be provided");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
this.options = options;
|
|
29
|
+
this.#recordFn = recordFn;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get isRecording() {
|
|
33
|
+
return this.#stopFn !== null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get options() {
|
|
37
|
+
return this.#options;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
set options(newOptions) {
|
|
41
|
+
this.configure(newOptions);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
configure(newOptions) {
|
|
45
|
+
const {
|
|
46
|
+
// Rollbar options
|
|
47
|
+
enabled,
|
|
48
|
+
autoStart,
|
|
49
|
+
maxSeconds,
|
|
50
|
+
triggerOptions,
|
|
51
|
+
debug,
|
|
52
|
+
|
|
53
|
+
// disallowed rrweb options
|
|
54
|
+
emit,
|
|
55
|
+
checkoutEveryNms,
|
|
56
|
+
|
|
57
|
+
// rrweb options
|
|
58
|
+
...rrwebOptions
|
|
59
|
+
} = newOptions;
|
|
60
|
+
this.#options = { enabled, autoStart, maxSeconds, triggerOptions, debug };
|
|
61
|
+
this.#rrwebOptions = rrwebOptions;
|
|
62
|
+
|
|
63
|
+
if (this.isRecording && newOptions.enabled === false) {
|
|
64
|
+
this.stop();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
checkoutEveryNms() {
|
|
69
|
+
// Recording may be up to two checkout intervals, therefore the checkout
|
|
70
|
+
// interval is set to half of the maxSeconds.
|
|
71
|
+
return (this.options.maxSeconds || 10) * 1000 / 2;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Converts recorded events into a formatted payload ready for transport.
|
|
76
|
+
*
|
|
77
|
+
* This method takes the recorder's stored events, creates a new span with the
|
|
78
|
+
* provided tracing context, attaches all events with their timestamps as span
|
|
79
|
+
* events, and then returns a payload ready for transport to the server.
|
|
80
|
+
*
|
|
81
|
+
* @param {Object} tracing - The tracing system instance to create spans
|
|
82
|
+
* @param {string} replayId - Unique identifier to associate with this replay recording
|
|
83
|
+
* @returns {Object|null} A formatted payload containing spans data in OTLP format, or null if no events exist
|
|
84
|
+
*/
|
|
85
|
+
dump(tracing, replayId, occurrenceUuid) {
|
|
86
|
+
const events = this.#events.previous.concat(this.#events.current);
|
|
87
|
+
|
|
88
|
+
if (events.length < 2) {
|
|
89
|
+
logger.error('Replay recording cannot have less than 2 events');
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const recordingSpan = tracing.startSpan('rrweb-replay-recording', {});
|
|
94
|
+
|
|
95
|
+
recordingSpan.setAttribute('rollbar.replay.id', replayId);
|
|
96
|
+
|
|
97
|
+
if (occurrenceUuid) {
|
|
98
|
+
recordingSpan.setAttribute('rollbar.occurrence.uuid', occurrenceUuid);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const earliestEvent = events.reduce((earliestEvent, event) =>
|
|
102
|
+
event.timestamp < earliestEvent.timestamp ? event : earliestEvent,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
recordingSpan.span.startTime = hrtime.fromMillis(earliestEvent.timestamp);
|
|
106
|
+
|
|
107
|
+
for (const event of events) {
|
|
108
|
+
recordingSpan.addEvent(
|
|
109
|
+
'rrweb-replay-events',
|
|
110
|
+
{
|
|
111
|
+
eventType: event.type,
|
|
112
|
+
json: JSON.stringify(event.data),
|
|
113
|
+
'rollbar.replay.id': replayId,
|
|
114
|
+
},
|
|
115
|
+
hrtime.fromMillis(event.timestamp),
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
recordingSpan.end();
|
|
120
|
+
|
|
121
|
+
return tracing.exporter.toPayload();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
start() {
|
|
125
|
+
if (this.isRecording || this.options.enabled === false) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
this.clear();
|
|
130
|
+
|
|
131
|
+
this.#stopFn = this.#recordFn({
|
|
132
|
+
emit: (event, isCheckout) => {
|
|
133
|
+
if (this.options.debug?.logEmits) {
|
|
134
|
+
this._logEvent(event, isCheckout);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (isCheckout && event.type === EventType.Meta) {
|
|
138
|
+
this.#events.previous = this.#events.current;
|
|
139
|
+
this.#events.current = [];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
this.#events.current.push(event);
|
|
143
|
+
},
|
|
144
|
+
checkoutEveryNms: this.checkoutEveryNms(),
|
|
145
|
+
errorHandler: (error) => {
|
|
146
|
+
if (this.options.debug?.logErrors) {
|
|
147
|
+
logger.error('Error during replay recording', error);
|
|
148
|
+
}
|
|
149
|
+
return true; // swallow the error instead of throwing it to the window
|
|
150
|
+
},
|
|
151
|
+
...this.#rrwebOptions,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
return this;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
stop() {
|
|
158
|
+
if (!this.isRecording) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this.#stopFn();
|
|
163
|
+
this.#stopFn = null;
|
|
164
|
+
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
clear() {
|
|
169
|
+
this.#events = {
|
|
170
|
+
previous: [],
|
|
171
|
+
current: [],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
_logEvent(event, isCheckout) {
|
|
176
|
+
logger.log(
|
|
177
|
+
`Recorder: ${isCheckout ? 'checkout' : ''} event\n`,
|
|
178
|
+
((e) => {
|
|
179
|
+
const seen = new WeakSet();
|
|
180
|
+
return JSON.stringify(
|
|
181
|
+
e,
|
|
182
|
+
(_, v) => {
|
|
183
|
+
if (typeof v === 'object' && v !== null) {
|
|
184
|
+
if (seen.has(v)) return '[Circular]';
|
|
185
|
+
seen.add(v);
|
|
186
|
+
}
|
|
187
|
+
return v;
|
|
188
|
+
},
|
|
189
|
+
2,
|
|
190
|
+
);
|
|
191
|
+
})(event),
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|