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
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common test utilities for server transforms tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import Rollbar from '../src/server/rollbar.js';
|
|
6
|
+
import merge from '../src/merge.js';
|
|
7
|
+
|
|
8
|
+
export class CustomError extends Rollbar.Error {
|
|
9
|
+
constructor(message, nested) {
|
|
10
|
+
super(message, nested);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const nodeVersion = process.versions.node
|
|
15
|
+
.split('.')
|
|
16
|
+
.map((v) => parseInt(v));
|
|
17
|
+
|
|
18
|
+
export const isMinNodeVersion = (major, minor) =>
|
|
19
|
+
nodeVersion[0] > major ||
|
|
20
|
+
(nodeVersion[0] === major && nodeVersion[1] >= minor);
|
|
21
|
+
|
|
22
|
+
export async function wait(ms) {
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
setTimeout(resolve, ms);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function throwInScriptFile(filepath) {
|
|
29
|
+
// Dynamic imports inside setTimeout with async/await can create timing
|
|
30
|
+
// issues where the error escapes before Rollbar's handler can catch it.
|
|
31
|
+
const module = await import(filepath);
|
|
32
|
+
setTimeout(() => module.default(), 10);
|
|
33
|
+
await wait(500);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Base test item factory for request-related tests
|
|
37
|
+
export function createTestItem(overrides = {}) {
|
|
38
|
+
const base = {
|
|
39
|
+
request: {
|
|
40
|
+
headers: {
|
|
41
|
+
host: 'example.com',
|
|
42
|
+
'x-auth-token': '12345',
|
|
43
|
+
},
|
|
44
|
+
protocol: 'https',
|
|
45
|
+
url: '/some/endpoint',
|
|
46
|
+
ip: '192.192.192.1',
|
|
47
|
+
method: 'GET',
|
|
48
|
+
body: {
|
|
49
|
+
token: 'abc123',
|
|
50
|
+
something: 'else',
|
|
51
|
+
},
|
|
52
|
+
user: {
|
|
53
|
+
id: 42,
|
|
54
|
+
email: 'fake@example.com',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
stuff: 'hey',
|
|
58
|
+
data: { other: 'thing' },
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return merge(base, overrides);
|
|
62
|
+
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/* globals describe */
|
|
2
|
+
/* globals it */
|
|
3
|
+
|
|
4
|
+
import { expect } from 'chai';
|
|
5
|
+
import Transport from '../src/server/transport.js';
|
|
6
|
+
|
|
7
|
+
class TestTransport {
|
|
8
|
+
constructor(options, error, response, assertions) {
|
|
9
|
+
this.options = options;
|
|
10
|
+
this.error = error;
|
|
11
|
+
this.response = response;
|
|
12
|
+
this.requestOpts = null;
|
|
13
|
+
this.requestCallback = null;
|
|
14
|
+
this.assertions = assertions;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
request(opts, cb) {
|
|
18
|
+
this.requestOpts = opts;
|
|
19
|
+
this.requestCallback = cb;
|
|
20
|
+
return new TestRequest(this.error, this.response, this);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class TestRequest {
|
|
25
|
+
constructor(error, response, transport) {
|
|
26
|
+
this.error = error;
|
|
27
|
+
this.responseData = response;
|
|
28
|
+
this.data = [];
|
|
29
|
+
this.events = {};
|
|
30
|
+
this.transport = transport;
|
|
31
|
+
this.response = null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
write(data) {
|
|
35
|
+
this.data.push(data);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
on(event, cb) {
|
|
39
|
+
this.events[event] = cb;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
end() {
|
|
43
|
+
if (this.transport.assertions) {
|
|
44
|
+
this.transport.assertions();
|
|
45
|
+
}
|
|
46
|
+
if (this.error) {
|
|
47
|
+
if (this.events['error']) {
|
|
48
|
+
this.events['error'](this.error);
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
this.response = new TestResponse();
|
|
52
|
+
this.transport.requestCallback(this.response);
|
|
53
|
+
if (this.response.events['data']) {
|
|
54
|
+
this.response.events['data'](this.responseData);
|
|
55
|
+
}
|
|
56
|
+
if (this.response.events['end']) {
|
|
57
|
+
this.response.events['end']();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
class TestResponse {
|
|
64
|
+
constructor(options = {}) {
|
|
65
|
+
this.encoding = null;
|
|
66
|
+
this.events = {};
|
|
67
|
+
this.headers = options.headers || {};
|
|
68
|
+
this.statusCode = options.statusCode || 200;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
setEncoding(s) {
|
|
72
|
+
this.encoding = s;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
on(event, cb) {
|
|
76
|
+
this.events[event] = cb;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const transportFactory = (error, response, assertions) => (options) =>
|
|
81
|
+
new TestTransport(options, error, response, assertions);
|
|
82
|
+
|
|
83
|
+
describe('transport', function () {
|
|
84
|
+
const t = new Transport();
|
|
85
|
+
|
|
86
|
+
describe('post', function () {
|
|
87
|
+
describe('base data', function () {
|
|
88
|
+
const baseData = {
|
|
89
|
+
accessToken: 'abc123',
|
|
90
|
+
options: {},
|
|
91
|
+
payload: {
|
|
92
|
+
access_token: 'abc123',
|
|
93
|
+
data: { a: 1 },
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
it('should have an error with no payload', function (done) {
|
|
98
|
+
const factory = transportFactory(
|
|
99
|
+
null,
|
|
100
|
+
'{"err": null, "result":"all good"}',
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
t.post(
|
|
104
|
+
baseData.accessToken,
|
|
105
|
+
baseData.options,
|
|
106
|
+
null,
|
|
107
|
+
(err, resp) => {
|
|
108
|
+
expect(err).to.exist;
|
|
109
|
+
expect(resp).to.not.exist;
|
|
110
|
+
done();
|
|
111
|
+
},
|
|
112
|
+
factory,
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should have the right response data with a payload and no error', function (done) {
|
|
117
|
+
const factory = transportFactory(
|
|
118
|
+
null,
|
|
119
|
+
'{"err": null, "result":{"uuid":"42def", "message":"all good"}}',
|
|
120
|
+
function () {
|
|
121
|
+
expect(this.options.headers['Content-Type']).to.equal(
|
|
122
|
+
'application/json',
|
|
123
|
+
);
|
|
124
|
+
expect(this.options.headers['Content-Length']).to.be.a('number');
|
|
125
|
+
expect(this.options.headers['Content-Length']).to.be.above(0);
|
|
126
|
+
expect(this.options.headers['X-Rollbar-Access-Token']).to.equal(
|
|
127
|
+
baseData.accessToken,
|
|
128
|
+
);
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
t.post(
|
|
133
|
+
baseData.accessToken,
|
|
134
|
+
baseData.options,
|
|
135
|
+
baseData.payload,
|
|
136
|
+
(err, resp) => {
|
|
137
|
+
expect(err).to.not.exist;
|
|
138
|
+
expect(resp.message).to.equal('all good');
|
|
139
|
+
done();
|
|
140
|
+
},
|
|
141
|
+
factory,
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should error with a payload and an error in the response', function (done) {
|
|
146
|
+
const factory = transportFactory(
|
|
147
|
+
null,
|
|
148
|
+
'{"err": "bork", "message":"things broke"}',
|
|
149
|
+
function () {
|
|
150
|
+
expect(this.options.headers['Content-Type']).to.equal(
|
|
151
|
+
'application/json',
|
|
152
|
+
);
|
|
153
|
+
expect(this.options.headers['Content-Length']).to.be.a('number');
|
|
154
|
+
expect(this.options.headers['Content-Length']).to.be.above(0);
|
|
155
|
+
expect(this.options.headers['X-Rollbar-Access-Token']).to.equal(
|
|
156
|
+
baseData.accessToken,
|
|
157
|
+
);
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
t.post(
|
|
162
|
+
baseData.accessToken,
|
|
163
|
+
baseData.options,
|
|
164
|
+
baseData.payload,
|
|
165
|
+
(err, resp) => {
|
|
166
|
+
expect(err).to.exist;
|
|
167
|
+
expect(err.message).to.match(/things broke/);
|
|
168
|
+
expect(resp).to.not.exist;
|
|
169
|
+
done();
|
|
170
|
+
},
|
|
171
|
+
factory,
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should error with a payload and an error during sending', function (done) {
|
|
176
|
+
const factory = transportFactory(new Error('bork'), null, function () {
|
|
177
|
+
expect(this.options.headers['Content-Type']).to.equal(
|
|
178
|
+
'application/json',
|
|
179
|
+
);
|
|
180
|
+
expect(this.options.headers['Content-Length']).to.be.a('number');
|
|
181
|
+
expect(this.options.headers['Content-Length']).to.be.above(0);
|
|
182
|
+
expect(this.options.headers['X-Rollbar-Access-Token']).to.equal(
|
|
183
|
+
baseData.accessToken,
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
t.post(
|
|
188
|
+
baseData.accessToken,
|
|
189
|
+
baseData.options,
|
|
190
|
+
baseData.payload,
|
|
191
|
+
(err, resp) => {
|
|
192
|
+
expect(err).to.exist;
|
|
193
|
+
expect(err.message).to.match(/bork/);
|
|
194
|
+
expect(resp).to.not.exist;
|
|
195
|
+
done();
|
|
196
|
+
},
|
|
197
|
+
factory,
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe('with rate limiting', function () {
|
|
203
|
+
let transport;
|
|
204
|
+
|
|
205
|
+
beforeEach(function () {
|
|
206
|
+
transport = new Transport();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should transmit non-rate limited requests', function (done) {
|
|
210
|
+
const factory = transportFactory(
|
|
211
|
+
null,
|
|
212
|
+
'{"err": null, "result": "all good"}',
|
|
213
|
+
);
|
|
214
|
+
const response = new TestResponse({
|
|
215
|
+
statusCode: 200,
|
|
216
|
+
headers: {
|
|
217
|
+
'x-rate-limit-remaining': '1',
|
|
218
|
+
'x-rate-limit-remaining-seconds': '100',
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
expect(transport.rateLimitExpires).to.equal(0);
|
|
223
|
+
|
|
224
|
+
transport.handleResponse(response);
|
|
225
|
+
|
|
226
|
+
transport.post(
|
|
227
|
+
'token',
|
|
228
|
+
{},
|
|
229
|
+
'payload',
|
|
230
|
+
(err) => {
|
|
231
|
+
expect(err).to.not.exist;
|
|
232
|
+
expect(Math.floor(Date.now() / 1000)).to.be.at.least(
|
|
233
|
+
transport.rateLimitExpires,
|
|
234
|
+
);
|
|
235
|
+
done();
|
|
236
|
+
},
|
|
237
|
+
factory,
|
|
238
|
+
);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should drop rate limited requests and set timeout', function (done) {
|
|
242
|
+
const factory = transportFactory(new Error('bork'), null);
|
|
243
|
+
const response = new TestResponse({
|
|
244
|
+
statusCode: 429,
|
|
245
|
+
headers: {
|
|
246
|
+
'x-rate-limit-remaining': '0',
|
|
247
|
+
'x-rate-limit-remaining-seconds': '100',
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
transport.handleResponse(response);
|
|
252
|
+
|
|
253
|
+
transport.post(
|
|
254
|
+
'token',
|
|
255
|
+
{},
|
|
256
|
+
'payload',
|
|
257
|
+
(err) => {
|
|
258
|
+
expect(err.message).to.match(/Exceeded rate limit/);
|
|
259
|
+
expect(Math.floor(Date.now() / 1000)).to.be.below(
|
|
260
|
+
transport.rateLimitExpires,
|
|
261
|
+
);
|
|
262
|
+
done();
|
|
263
|
+
},
|
|
264
|
+
factory,
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
});
|
package/test/telemetry.test.js
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
/* globals it */
|
|
4
4
|
/* globals sinon */
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
import Telemeter from '../src/telemetry.js';
|
|
7
|
+
import Tracing from '../src/tracing/tracing.js';
|
|
7
8
|
|
|
8
|
-
describe('Telemetry()', function() {
|
|
9
|
-
it('should have all of the expected methods', function(done) {
|
|
9
|
+
describe('Telemetry()', function () {
|
|
10
|
+
it('should have all of the expected methods', function (done) {
|
|
10
11
|
var options = {};
|
|
11
12
|
var t = new Telemeter(options);
|
|
12
13
|
expect(t).to.have.property('copyEvents');
|
|
@@ -20,12 +21,12 @@ describe('Telemetry()', function() {
|
|
|
20
21
|
});
|
|
21
22
|
});
|
|
22
23
|
|
|
23
|
-
describe('capture', function() {
|
|
24
|
-
it('should return a valid telemetry event', function(done) {
|
|
24
|
+
describe('capture', function () {
|
|
25
|
+
it('should return a valid telemetry event', function (done) {
|
|
25
26
|
var options = {};
|
|
26
27
|
var t = new Telemeter(options);
|
|
27
28
|
var now = +new Date();
|
|
28
|
-
var event = t.capture('network', {url: 'a.com'}, 'debug');
|
|
29
|
+
var event = t.capture('network', { url: 'a.com' }, 'debug');
|
|
29
30
|
expect(event.timestamp_ms - now).to.be.below(500);
|
|
30
31
|
expect(event.type).to.equal('network');
|
|
31
32
|
expect(event.level).to.equal('debug');
|
|
@@ -35,10 +36,10 @@ describe('capture', function() {
|
|
|
35
36
|
});
|
|
36
37
|
});
|
|
37
38
|
|
|
38
|
-
describe('captureEvent', function() {
|
|
39
|
-
it('should return a valid telemetry event', function(done) {
|
|
39
|
+
describe('captureEvent', function () {
|
|
40
|
+
it('should return a valid telemetry event', function (done) {
|
|
40
41
|
var t = new Telemeter();
|
|
41
|
-
var event = t.captureEvent('log', {message: 'bar'}, 'info');
|
|
42
|
+
var event = t.captureEvent('log', { message: 'bar' }, 'info');
|
|
42
43
|
expect(event.type).to.equal('log');
|
|
43
44
|
expect(event.level).to.equal('info');
|
|
44
45
|
expect(event.body.message).to.equal('bar');
|
|
@@ -47,32 +48,171 @@ describe('captureEvent', function() {
|
|
|
47
48
|
});
|
|
48
49
|
});
|
|
49
50
|
|
|
50
|
-
describe('
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
describe('capture events', function () {
|
|
52
|
+
beforeEach(function () {
|
|
53
|
+
const tracing = new Tracing(
|
|
54
|
+
window,
|
|
55
|
+
{
|
|
56
|
+
resource: {
|
|
57
|
+
'service.name': 'Test',
|
|
58
|
+
},
|
|
57
59
|
}
|
|
60
|
+
);
|
|
61
|
+
tracing.initSession();
|
|
62
|
+
this.t = new Telemeter({includeItemsInTelemetry: true}, tracing);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should return a valid log event', function (done) {
|
|
66
|
+
const timestamp = 12345.678;
|
|
67
|
+
const event = this.t.captureLog('foo', 'info', null, timestamp);
|
|
68
|
+
expect(event.type).to.equal('log');
|
|
69
|
+
expect(event.level).to.equal('info');
|
|
70
|
+
expect(event.body.message).to.equal('foo');
|
|
71
|
+
expect(event.timestamp_ms).to.equal(timestamp);
|
|
72
|
+
|
|
73
|
+
expect(this.t.telemetrySpan).to.be.an('object');
|
|
74
|
+
const otelEvent = this.t.telemetrySpan.span.events[0];
|
|
75
|
+
expect(otelEvent.name).to.equal('rollbar-log-event');
|
|
76
|
+
expect(otelEvent.time).to.eql([ 12, 345678000 ]);
|
|
77
|
+
expect(otelEvent.attributes).to.eql({ message: 'foo', level: 'info' });
|
|
78
|
+
done();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should return a valid error event', function (done) {
|
|
82
|
+
const timestamp = 12345.678;
|
|
83
|
+
const error = new Error('foo');
|
|
84
|
+
const uuid = '12345-67890';
|
|
85
|
+
const event = this.t.captureError(error, 'info', uuid, timestamp);
|
|
86
|
+
expect(event.type).to.eql('error');
|
|
87
|
+
expect(event.level).to.equal('info');
|
|
88
|
+
expect(event.body.message).to.equal('foo');
|
|
89
|
+
expect(event.timestamp_ms).to.equal(timestamp);
|
|
90
|
+
|
|
91
|
+
expect(this.t.telemetrySpan).to.be.an('object');
|
|
92
|
+
const otelEvent = this.t.telemetrySpan.span.events[0];
|
|
93
|
+
expect(otelEvent.name).to.eql('rollbar-occurrence-event');
|
|
94
|
+
expect(otelEvent.time).to.eql([ 12, 345678000 ]);
|
|
95
|
+
expect(otelEvent.attributes).to.eql(
|
|
96
|
+
{ type: 'error', 'occurrence.type': 'error', message: 'foo', level: 'info', uuid: uuid, 'occurrence.uuid': uuid }
|
|
97
|
+
);
|
|
98
|
+
done();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should return a valid message event', function (done) {
|
|
102
|
+
const timestamp = 12345.678;
|
|
103
|
+
const uuid = '12345-67890';
|
|
104
|
+
const item = { message: 'foo', level: 'info', uuid: uuid, timestamp: timestamp };
|
|
105
|
+
const event = this.t._captureRollbarItem(item);
|
|
106
|
+
expect(event.type).to.eql('log');
|
|
107
|
+
expect(event.level).to.equal('info');
|
|
108
|
+
expect(event.body.message).to.equal('foo');
|
|
109
|
+
expect(event.timestamp_ms).to.equal(timestamp);
|
|
110
|
+
|
|
111
|
+
expect(this.t.telemetrySpan).to.be.an('object');
|
|
112
|
+
const otelEvent = this.t.telemetrySpan.span.events[0];
|
|
113
|
+
expect(otelEvent.name).to.eql('rollbar-occurrence-event');
|
|
114
|
+
expect(otelEvent.time).to.eql([ 12, 345678000 ]);
|
|
115
|
+
expect(otelEvent.attributes).to.eql(
|
|
116
|
+
{ type: 'message', 'occurrence.type': 'message', message: 'foo', level: 'info', uuid: uuid, 'occurrence.uuid': uuid }
|
|
117
|
+
);
|
|
118
|
+
done();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should return a valid navigation event', function (done) {
|
|
122
|
+
const timestamp = 12345.678;
|
|
123
|
+
const from = 'foo';
|
|
124
|
+
const to = 'bar';
|
|
125
|
+
const event = this.t.captureNavigation(from, to, null, timestamp);
|
|
126
|
+
expect(event.type).to.equal('navigation');
|
|
127
|
+
expect(event.level).to.equal('info');
|
|
128
|
+
expect(event.body.from).to.equal('foo');
|
|
129
|
+
expect(event.body.to).to.equal('bar');
|
|
130
|
+
expect(event.timestamp_ms).to.equal(timestamp);
|
|
131
|
+
|
|
132
|
+
expect(this.t.telemetrySpan).to.be.an('object');
|
|
133
|
+
const otelEvent = this.t.telemetrySpan.span.events[0];
|
|
134
|
+
expect(otelEvent.name).to.equal('rollbar-navigation-event');
|
|
135
|
+
expect(otelEvent.time).to.eql([ 12, 345678000 ]);
|
|
136
|
+
expect(otelEvent.attributes).to.eql({ 'previous.url.full': 'foo', 'url.full': 'bar' });
|
|
137
|
+
done();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should return a valid network event', function (done) {
|
|
141
|
+
const subtype = 'xhr';
|
|
142
|
+
const timestamp = 12345.678;
|
|
143
|
+
const metadata = {
|
|
144
|
+
url: 'https://example.com',
|
|
145
|
+
method: 'GET',
|
|
146
|
+
status_code: 400,
|
|
147
|
+
request_headers: { 'Content-Type': 'application/json' },
|
|
148
|
+
response: {
|
|
149
|
+
headers: { 'Content-Type': 'application/json' },
|
|
150
|
+
},
|
|
151
|
+
start_time_ms: timestamp,
|
|
152
|
+
end_time_ms: 12345678,
|
|
153
|
+
};
|
|
154
|
+
const event = this.t.captureNetwork(metadata, subtype, null, null);
|
|
155
|
+
expect(event.type).to.eql('network');
|
|
156
|
+
expect(event.level).to.equal('error');
|
|
157
|
+
expect(event.body.url).to.equal(metadata.url);
|
|
158
|
+
expect(event.body.method).to.equal(metadata.method);
|
|
159
|
+
expect(event.body.status_code).to.equal(metadata.status_code);
|
|
160
|
+
expect(event.timestamp_ms).to.equal(timestamp);
|
|
161
|
+
|
|
162
|
+
expect(this.t.telemetrySpan).to.be.an('object');
|
|
163
|
+
const otelEvent = this.t.telemetrySpan.span.events[0];
|
|
164
|
+
expect(otelEvent.name).to.eql('rollbar-network-event');
|
|
165
|
+
expect(otelEvent.time).to.eql([ 12, 345678000 ]);
|
|
166
|
+
expect(otelEvent.attributes).to.eql(
|
|
167
|
+
{
|
|
168
|
+
type: subtype,
|
|
169
|
+
method: metadata.method,
|
|
170
|
+
statusCode: metadata.status_code,
|
|
171
|
+
url: metadata.url,
|
|
172
|
+
'request.headers': JSON.stringify(metadata.request_headers),
|
|
173
|
+
'response.headers': JSON.stringify(metadata.response.headers),
|
|
174
|
+
'response.timeUnixNano': (metadata.end_time_ms * 1e6).toString(),
|
|
175
|
+
}
|
|
176
|
+
);
|
|
177
|
+
done();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe('filterTelemetry', function () {
|
|
182
|
+
it("should filter out events that don't match the test", function (done) {
|
|
183
|
+
var options = {
|
|
184
|
+
filterTelemetry: function (e) {
|
|
185
|
+
return (
|
|
186
|
+
e.type === 'network' &&
|
|
187
|
+
(e.body.subtype === 'xhr' || e.body.subtype === 'fetch') &&
|
|
188
|
+
e.body.url.indexOf('https://spammer.com') === 0
|
|
189
|
+
);
|
|
190
|
+
},
|
|
58
191
|
};
|
|
59
192
|
var t = new Telemeter(options);
|
|
60
|
-
var evt = t.capture(
|
|
193
|
+
var evt = t.capture(
|
|
194
|
+
'network',
|
|
195
|
+
{ url: 'https://spammer.com', subtype: 'xhr' },
|
|
196
|
+
'debug',
|
|
197
|
+
);
|
|
61
198
|
expect(evt).to.be(false);
|
|
62
199
|
|
|
63
200
|
done();
|
|
64
201
|
});
|
|
65
202
|
|
|
66
|
-
it('should filter out events in copy even if they are modified after capture', function(done) {
|
|
203
|
+
it('should filter out events in copy even if they are modified after capture', function (done) {
|
|
67
204
|
var options = {
|
|
68
|
-
filterTelemetry: function(e) {
|
|
69
|
-
return e.type === 'network'
|
|
70
|
-
|
|
71
|
-
}
|
|
205
|
+
filterTelemetry: function (e) {
|
|
206
|
+
return e.type === 'network' && e.body.statusCode === 200;
|
|
207
|
+
},
|
|
72
208
|
};
|
|
73
209
|
var t = new Telemeter(options);
|
|
74
|
-
var evt = t.capture('network', {url: 'https://spammer.com'}, 'debug');
|
|
75
|
-
var evt2 = t.capture(
|
|
210
|
+
var evt = t.capture('network', { url: 'https://spammer.com' }, 'debug');
|
|
211
|
+
var evt2 = t.capture(
|
|
212
|
+
'network',
|
|
213
|
+
{ url: 'https://spammer.com', statusCode: 404 },
|
|
214
|
+
'debug',
|
|
215
|
+
);
|
|
76
216
|
expect(evt).not.to.be(false);
|
|
77
217
|
expect(evt2).not.to.be(false);
|
|
78
218
|
var events = t.copyEvents();
|
|
@@ -88,50 +228,50 @@ describe('filterTelemetry', function() {
|
|
|
88
228
|
});
|
|
89
229
|
});
|
|
90
230
|
|
|
91
|
-
describe('configure', function() {
|
|
92
|
-
it('should truncate events to new max', function(done) {
|
|
93
|
-
var options = {maxTelemetryEvents: 5};
|
|
231
|
+
describe('configure', function () {
|
|
232
|
+
it('should truncate events to new max', function (done) {
|
|
233
|
+
var options = { maxTelemetryEvents: 5 };
|
|
94
234
|
var t = new Telemeter(options);
|
|
95
235
|
|
|
96
236
|
for (var i = 0; i < 7; i++) {
|
|
97
|
-
t.capture('network', {url: 'a.com'}, 'debug');
|
|
237
|
+
t.capture('network', { url: 'a.com' }, 'debug');
|
|
98
238
|
}
|
|
99
239
|
|
|
100
240
|
expect(t.queue.length).to.equal(5);
|
|
101
|
-
t.configure({maxTelemetryEvents: 3});
|
|
241
|
+
t.configure({ maxTelemetryEvents: 3 });
|
|
102
242
|
expect(t.queue.length).to.equal(3);
|
|
103
243
|
done();
|
|
104
244
|
});
|
|
105
|
-
it('should lengthen events to allow new max', function(done) {
|
|
106
|
-
var options = {maxTelemetryEvents: 3};
|
|
245
|
+
it('should lengthen events to allow new max', function (done) {
|
|
246
|
+
var options = { maxTelemetryEvents: 3 };
|
|
107
247
|
var t = new Telemeter(options);
|
|
108
248
|
|
|
109
249
|
for (var i = 0; i < 7; i++) {
|
|
110
|
-
t.capture('network', {url: 'a.com'}, 'debug');
|
|
250
|
+
t.capture('network', { url: 'a.com' }, 'debug');
|
|
111
251
|
}
|
|
112
252
|
|
|
113
253
|
expect(t.queue.length).to.equal(3);
|
|
114
|
-
t.configure({maxTelemetryEvents: 5});
|
|
254
|
+
t.configure({ maxTelemetryEvents: 5 });
|
|
115
255
|
expect(t.queue.length).to.equal(3);
|
|
116
256
|
for (var i = 0; i < 7; i++) {
|
|
117
|
-
t.capture('network', {url: 'a.com'}, 'debug');
|
|
257
|
+
t.capture('network', { url: 'a.com' }, 'debug');
|
|
118
258
|
}
|
|
119
259
|
expect(t.queue.length).to.equal(5);
|
|
120
260
|
done();
|
|
121
261
|
});
|
|
122
|
-
it('does not drop existing options that are not passed to configure', function(done) {
|
|
123
|
-
var options = {maxTelemetryEvents: 3};
|
|
262
|
+
it('does not drop existing options that are not passed to configure', function (done) {
|
|
263
|
+
var options = { maxTelemetryEvents: 3 };
|
|
124
264
|
var t = new Telemeter(options);
|
|
125
265
|
|
|
126
266
|
for (var i = 0; i < 7; i++) {
|
|
127
|
-
t.capture('network', {url: 'a.com'}, 'debug');
|
|
267
|
+
t.capture('network', { url: 'a.com' }, 'debug');
|
|
128
268
|
}
|
|
129
269
|
|
|
130
270
|
expect(t.queue.length).to.equal(3);
|
|
131
271
|
t.configure({});
|
|
132
272
|
expect(t.queue.length).to.equal(3);
|
|
133
273
|
for (var i = 0; i < 7; i++) {
|
|
134
|
-
t.capture('network', {url: 'a.com'}, 'debug');
|
|
274
|
+
t.capture('network', { url: 'a.com' }, 'debug');
|
|
135
275
|
}
|
|
136
276
|
expect(t.queue.length).to.equal(3);
|
|
137
277
|
done();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* globals describe */
|
|
2
|
+
/* globals it */
|
|
3
|
+
|
|
4
|
+
import { expect } from 'chai';
|
|
5
|
+
|
|
6
|
+
import { ContextManager, createContextKey } from '../../src/tracing/contextManager.js';
|
|
7
|
+
import { ROOT_CONTEXT } from '../../src/tracing/context.js';
|
|
8
|
+
|
|
9
|
+
describe('ContextManager()', function () {
|
|
10
|
+
it('should process pending spans', function (done) {
|
|
11
|
+
const SPAN_KEY = createContextKey('Rollbar Context Key SPAN');
|
|
12
|
+
const contextManager = new ContextManager();
|
|
13
|
+
|
|
14
|
+
expect(contextManager.active()).to.equal(ROOT_CONTEXT);
|
|
15
|
+
|
|
16
|
+
let context = contextManager.active().setValue(SPAN_KEY, 'a')
|
|
17
|
+
let prevContext = contextManager.enterContext(context);
|
|
18
|
+
|
|
19
|
+
expect(prevContext).to.equal(ROOT_CONTEXT);
|
|
20
|
+
expect(contextManager.active()).to.equal(context);
|
|
21
|
+
|
|
22
|
+
contextManager.exitContext(prevContext);
|
|
23
|
+
|
|
24
|
+
expect(contextManager.active()).to.equal(ROOT_CONTEXT);
|
|
25
|
+
|
|
26
|
+
done();
|
|
27
|
+
});
|
|
28
|
+
});
|