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/telemetry.js
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
var _ = require('./utility');
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const MAX_EVENTS = 100;
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// Temporary workaround while solving commonjs -> esm issues in Node 18 - 20.
|
|
6
|
+
function fromMillis(millis) {
|
|
7
|
+
return [Math.trunc(millis / 1000), Math.round((millis % 1000) * 1e6)];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function Telemeter(options, tracing) {
|
|
6
11
|
this.queue = [];
|
|
7
12
|
this.options = _.merge(options);
|
|
8
13
|
var maxTelemetryEvents = this.options.maxTelemetryEvents || MAX_EVENTS;
|
|
9
14
|
this.maxQueueSize = Math.max(0, Math.min(maxTelemetryEvents, MAX_EVENTS));
|
|
15
|
+
this.tracing = tracing;
|
|
16
|
+
this.telemetrySpan = this.tracing?.startSpan('rollbar-telemetry', {});
|
|
10
17
|
}
|
|
11
18
|
|
|
12
|
-
Telemeter.prototype.configure = function(options) {
|
|
19
|
+
Telemeter.prototype.configure = function (options) {
|
|
13
20
|
var oldOptions = this.options;
|
|
14
21
|
this.options = _.merge(oldOptions, options);
|
|
15
22
|
var maxTelemetryEvents = this.options.maxTelemetryEvents || MAX_EVENTS;
|
|
@@ -22,7 +29,7 @@ Telemeter.prototype.configure = function(options) {
|
|
|
22
29
|
this.queue.splice(0, deleteCount);
|
|
23
30
|
};
|
|
24
31
|
|
|
25
|
-
Telemeter.prototype.copyEvents = function() {
|
|
32
|
+
Telemeter.prototype.copyEvents = function () {
|
|
26
33
|
var events = Array.prototype.slice.call(this.queue, 0);
|
|
27
34
|
if (_.isFunction(this.options.filterTelemetry)) {
|
|
28
35
|
try {
|
|
@@ -39,20 +46,29 @@ Telemeter.prototype.copyEvents = function() {
|
|
|
39
46
|
return events;
|
|
40
47
|
};
|
|
41
48
|
|
|
42
|
-
Telemeter.prototype.capture = function(
|
|
49
|
+
Telemeter.prototype.capture = function (
|
|
50
|
+
type,
|
|
51
|
+
metadata,
|
|
52
|
+
level,
|
|
53
|
+
rollbarUUID,
|
|
54
|
+
timestamp,
|
|
55
|
+
) {
|
|
43
56
|
var e = {
|
|
44
57
|
level: getLevel(type, level),
|
|
45
58
|
type: type,
|
|
46
59
|
timestamp_ms: timestamp || _.now(),
|
|
47
60
|
body: metadata,
|
|
48
|
-
source: 'client'
|
|
61
|
+
source: 'client',
|
|
49
62
|
};
|
|
50
63
|
if (rollbarUUID) {
|
|
51
64
|
e.uuid = rollbarUUID;
|
|
52
65
|
}
|
|
53
66
|
|
|
54
67
|
try {
|
|
55
|
-
if (
|
|
68
|
+
if (
|
|
69
|
+
_.isFunction(this.options.filterTelemetry) &&
|
|
70
|
+
this.options.filterTelemetry(e)
|
|
71
|
+
) {
|
|
56
72
|
return false;
|
|
57
73
|
}
|
|
58
74
|
} catch (exc) {
|
|
@@ -63,37 +79,114 @@ Telemeter.prototype.capture = function(type, metadata, level, rollbarUUID, times
|
|
|
63
79
|
return e;
|
|
64
80
|
};
|
|
65
81
|
|
|
66
|
-
Telemeter.prototype.captureEvent = function(
|
|
82
|
+
Telemeter.prototype.captureEvent = function (
|
|
83
|
+
type,
|
|
84
|
+
metadata,
|
|
85
|
+
level,
|
|
86
|
+
rollbarUUID,
|
|
87
|
+
) {
|
|
67
88
|
return this.capture(type, metadata, level, rollbarUUID);
|
|
68
89
|
};
|
|
69
90
|
|
|
70
|
-
Telemeter.prototype.captureError = function(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
91
|
+
Telemeter.prototype.captureError = function (
|
|
92
|
+
err,
|
|
93
|
+
level,
|
|
94
|
+
rollbarUUID,
|
|
95
|
+
timestamp,
|
|
96
|
+
) {
|
|
97
|
+
const message = err.message || String(err);
|
|
98
|
+
var metadata = {message};
|
|
74
99
|
if (err.stack) {
|
|
75
100
|
metadata.stack = err.stack;
|
|
76
101
|
}
|
|
102
|
+
this.telemetrySpan?.addEvent(
|
|
103
|
+
'rollbar-occurrence-event',
|
|
104
|
+
{
|
|
105
|
+
message,
|
|
106
|
+
level,
|
|
107
|
+
type: 'error',
|
|
108
|
+
uuid: rollbarUUID,
|
|
109
|
+
'occurrence.type': 'error', // deprecated
|
|
110
|
+
'occurrence.uuid': rollbarUUID, // deprecated
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
fromMillis(timestamp),
|
|
114
|
+
);
|
|
115
|
+
|
|
77
116
|
return this.capture('error', metadata, level, rollbarUUID, timestamp);
|
|
78
117
|
};
|
|
79
118
|
|
|
80
|
-
Telemeter.prototype.captureLog = function(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
119
|
+
Telemeter.prototype.captureLog = function (
|
|
120
|
+
message,
|
|
121
|
+
level,
|
|
122
|
+
rollbarUUID,
|
|
123
|
+
timestamp,
|
|
124
|
+
) {
|
|
125
|
+
// If the uuid is present, this is a message occurrence.
|
|
126
|
+
if (rollbarUUID) {
|
|
127
|
+
this.telemetrySpan?.addEvent(
|
|
128
|
+
'rollbar-occurrence-event',
|
|
129
|
+
{
|
|
130
|
+
message,
|
|
131
|
+
level,
|
|
132
|
+
type: 'message',
|
|
133
|
+
uuid: rollbarUUID,
|
|
134
|
+
'occurrence.type': 'message', // deprecated
|
|
135
|
+
'occurrence.uuid': rollbarUUID, // deprecated
|
|
136
|
+
},
|
|
137
|
+
fromMillis(timestamp),
|
|
138
|
+
);
|
|
139
|
+
} else {
|
|
140
|
+
this.telemetrySpan?.addEvent(
|
|
141
|
+
'rollbar-log-event',
|
|
142
|
+
{message, level},
|
|
143
|
+
fromMillis(timestamp),
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return this.capture(
|
|
148
|
+
'log',
|
|
149
|
+
{message},
|
|
150
|
+
level,
|
|
151
|
+
rollbarUUID,
|
|
152
|
+
timestamp,
|
|
153
|
+
);
|
|
84
154
|
};
|
|
85
155
|
|
|
86
|
-
Telemeter.prototype.captureNetwork = function(
|
|
156
|
+
Telemeter.prototype.captureNetwork = function (
|
|
157
|
+
metadata,
|
|
158
|
+
subtype,
|
|
159
|
+
rollbarUUID,
|
|
160
|
+
requestData,
|
|
161
|
+
) {
|
|
87
162
|
subtype = subtype || 'xhr';
|
|
88
163
|
metadata.subtype = metadata.subtype || subtype;
|
|
89
164
|
if (requestData) {
|
|
90
165
|
metadata.request = requestData;
|
|
91
166
|
}
|
|
92
167
|
var level = this.levelFromStatus(metadata.status_code);
|
|
93
|
-
|
|
168
|
+
|
|
169
|
+
const endTimeNano = (metadata.end_time_ms || 0) * 1e6;
|
|
170
|
+
|
|
171
|
+
this.telemetrySpan?.addEvent(
|
|
172
|
+
'rollbar-network-event',
|
|
173
|
+
{
|
|
174
|
+
type: metadata.subtype,
|
|
175
|
+
method: metadata.method,
|
|
176
|
+
url : metadata.url,
|
|
177
|
+
statusCode : metadata.status_code,
|
|
178
|
+
'request.headers': JSON.stringify(metadata.request_headers || {}),
|
|
179
|
+
'response.headers': JSON.stringify(metadata.response?.headers || {}),
|
|
180
|
+
'response.timeUnixNano': endTimeNano.toString(),
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
fromMillis(metadata.start_time_ms)
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
return this.capture('network', metadata, level, rollbarUUID, metadata.start_time_ms);
|
|
94
187
|
};
|
|
95
188
|
|
|
96
|
-
Telemeter.prototype.levelFromStatus = function(statusCode) {
|
|
189
|
+
Telemeter.prototype.levelFromStatus = function (statusCode) {
|
|
97
190
|
if (statusCode >= 200 && statusCode < 400) {
|
|
98
191
|
return 'info';
|
|
99
192
|
}
|
|
@@ -103,10 +196,16 @@ Telemeter.prototype.levelFromStatus = function(statusCode) {
|
|
|
103
196
|
return 'info';
|
|
104
197
|
};
|
|
105
198
|
|
|
106
|
-
Telemeter.prototype.captureDom = function(
|
|
199
|
+
Telemeter.prototype.captureDom = function (
|
|
200
|
+
subtype,
|
|
201
|
+
element,
|
|
202
|
+
value,
|
|
203
|
+
checked,
|
|
204
|
+
rollbarUUID,
|
|
205
|
+
) {
|
|
107
206
|
var metadata = {
|
|
108
207
|
subtype: subtype,
|
|
109
|
-
element: element
|
|
208
|
+
element: element,
|
|
110
209
|
};
|
|
111
210
|
if (value !== undefined) {
|
|
112
211
|
metadata.value = value;
|
|
@@ -117,31 +216,55 @@ Telemeter.prototype.captureDom = function(subtype, element, value, checked, roll
|
|
|
117
216
|
return this.capture('dom', metadata, 'info', rollbarUUID);
|
|
118
217
|
};
|
|
119
218
|
|
|
120
|
-
Telemeter.prototype.captureNavigation = function(from, to, rollbarUUID) {
|
|
121
|
-
|
|
219
|
+
Telemeter.prototype.captureNavigation = function (from, to, rollbarUUID, timestamp) {
|
|
220
|
+
this.telemetrySpan?.addEvent(
|
|
221
|
+
'rollbar-navigation-event',
|
|
222
|
+
{'previous.url.full': from, 'url.full': to},
|
|
223
|
+
fromMillis(timestamp),
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
return this.capture(
|
|
227
|
+
'navigation',
|
|
228
|
+
{from, to},
|
|
229
|
+
'info',
|
|
230
|
+
rollbarUUID,
|
|
231
|
+
timestamp,
|
|
232
|
+
);
|
|
122
233
|
};
|
|
123
234
|
|
|
124
|
-
Telemeter.prototype.captureDomContentLoaded = function(ts) {
|
|
125
|
-
return this.capture(
|
|
235
|
+
Telemeter.prototype.captureDomContentLoaded = function (ts) {
|
|
236
|
+
return this.capture(
|
|
237
|
+
'navigation',
|
|
238
|
+
{ subtype: 'DOMContentLoaded' },
|
|
239
|
+
'info',
|
|
240
|
+
undefined,
|
|
241
|
+
ts && ts.getTime(),
|
|
242
|
+
);
|
|
126
243
|
/**
|
|
127
244
|
* If we decide to make this a dom event instead, then use the line below:
|
|
128
245
|
return this.capture('dom', {subtype: 'DOMContentLoaded'}, 'info', undefined, ts && ts.getTime());
|
|
129
246
|
*/
|
|
130
247
|
};
|
|
131
|
-
Telemeter.prototype.captureLoad = function(ts) {
|
|
132
|
-
return this.capture(
|
|
248
|
+
Telemeter.prototype.captureLoad = function (ts) {
|
|
249
|
+
return this.capture(
|
|
250
|
+
'navigation',
|
|
251
|
+
{ subtype: 'load' },
|
|
252
|
+
'info',
|
|
253
|
+
undefined,
|
|
254
|
+
ts && ts.getTime(),
|
|
255
|
+
);
|
|
133
256
|
/**
|
|
134
257
|
* If we decide to make this a dom event instead, then use the line below:
|
|
135
258
|
return this.capture('dom', {subtype: 'load'}, 'info', undefined, ts && ts.getTime());
|
|
136
259
|
*/
|
|
137
260
|
};
|
|
138
261
|
|
|
139
|
-
Telemeter.prototype.captureConnectivityChange = function(type, rollbarUUID) {
|
|
140
|
-
return this.captureNetwork({change: type}, 'connectivity', rollbarUUID);
|
|
262
|
+
Telemeter.prototype.captureConnectivityChange = function (type, rollbarUUID) {
|
|
263
|
+
return this.captureNetwork({ change: type }, 'connectivity', rollbarUUID);
|
|
141
264
|
};
|
|
142
265
|
|
|
143
266
|
// Only intended to be used internally by the notifier
|
|
144
|
-
Telemeter.prototype._captureRollbarItem = function(item) {
|
|
267
|
+
Telemeter.prototype._captureRollbarItem = function (item) {
|
|
145
268
|
if (!this.options.includeItemsInTelemetry) {
|
|
146
269
|
return;
|
|
147
270
|
}
|
|
@@ -152,11 +275,17 @@ Telemeter.prototype._captureRollbarItem = function(item) {
|
|
|
152
275
|
return this.captureLog(item.message, item.level, item.uuid, item.timestamp);
|
|
153
276
|
}
|
|
154
277
|
if (item.custom) {
|
|
155
|
-
return this.capture(
|
|
278
|
+
return this.capture(
|
|
279
|
+
'log',
|
|
280
|
+
item.custom,
|
|
281
|
+
item.level,
|
|
282
|
+
item.uuid,
|
|
283
|
+
item.timestamp,
|
|
284
|
+
);
|
|
156
285
|
}
|
|
157
286
|
};
|
|
158
287
|
|
|
159
|
-
Telemeter.prototype.push = function(e) {
|
|
288
|
+
Telemeter.prototype.push = function (e) {
|
|
160
289
|
this.queue.push(e);
|
|
161
290
|
if (this.queue.length > this.maxQueueSize) {
|
|
162
291
|
this.queue.shift();
|
|
@@ -169,7 +298,7 @@ function getLevel(type, level) {
|
|
|
169
298
|
}
|
|
170
299
|
var defaultLevel = {
|
|
171
300
|
error: 'error',
|
|
172
|
-
manual: 'info'
|
|
301
|
+
manual: 'info',
|
|
173
302
|
};
|
|
174
303
|
return defaultLevel[type] || 'info';
|
|
175
304
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export class Context {
|
|
2
|
+
constructor(parentContext) {
|
|
3
|
+
this._currentContext = parentContext ? new Map(parentContext) : new Map();
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
getValue(key) {
|
|
7
|
+
return this._currentContext.get(key);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
setValue (key, value) {
|
|
11
|
+
const context = new Context(this._currentContext);
|
|
12
|
+
context._currentContext.set(key, value);
|
|
13
|
+
return context;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
deleteValue(key) {
|
|
17
|
+
const context = new Context(self._currentContext);
|
|
18
|
+
context._currentContext.delete(key);
|
|
19
|
+
return context;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const ROOT_CONTEXT = new Context();
|
|
24
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ROOT_CONTEXT } from './context.js';
|
|
2
|
+
|
|
3
|
+
export class ContextManager {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.currentContext = ROOT_CONTEXT;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
active() {
|
|
9
|
+
return this.currentContext;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
enterContext(context) {
|
|
13
|
+
const previousContext = this.currentContext;
|
|
14
|
+
this.currentContext = context || ROOT_CONTEXT;
|
|
15
|
+
return previousContext;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
exitContext(context) {
|
|
19
|
+
this.currentContext = context;
|
|
20
|
+
return this.currentContext;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
with(context, fn, thisArg, ...args) {
|
|
24
|
+
const previousContext = this.enterContext(context);
|
|
25
|
+
try {
|
|
26
|
+
return fn.call(thisArg, ...args);
|
|
27
|
+
} finally {
|
|
28
|
+
this.exitContext(previousContext);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function createContextKey(key) {
|
|
34
|
+
// Use Symbol for OpenTelemetry compatibility.
|
|
35
|
+
return Symbol.for(key);
|
|
36
|
+
}
|
|
37
|
+
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import hrtime from './hrtime';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SpanExporter is responsible for exporting ReadableSpan objects
|
|
5
|
+
* and transforming them into the OTLP-compatible format.
|
|
6
|
+
*/
|
|
7
|
+
export class SpanExporter {
|
|
8
|
+
/**
|
|
9
|
+
* Export spans to the span export queue
|
|
10
|
+
*
|
|
11
|
+
* @param {Array} spans - Array of ReadableSpan objects to export
|
|
12
|
+
* @param {Function} _resultCallback - Optional callback (not used)
|
|
13
|
+
*/
|
|
14
|
+
export(spans, _resultCallback) {
|
|
15
|
+
console.log(spans); // console exporter, TODO: make optional
|
|
16
|
+
spanExportQueue.push(...spans);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Transforms an array of ReadableSpan objects into the OTLP format payload
|
|
21
|
+
* compatible with the Rollbar API. This follows the OpenTelemetry protocol
|
|
22
|
+
* specification for traces.
|
|
23
|
+
*
|
|
24
|
+
* @returns {Object} OTLP format payload for API transmission
|
|
25
|
+
*/
|
|
26
|
+
toPayload() {
|
|
27
|
+
const spans = spanExportQueue.slice();
|
|
28
|
+
spanExportQueue.length = 0;
|
|
29
|
+
|
|
30
|
+
if (!spans || !spans.length) {
|
|
31
|
+
return { resourceSpans: [] };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const resource = (spans[0] && spans[0].resource) || {};
|
|
35
|
+
|
|
36
|
+
const scopeMap = new Map();
|
|
37
|
+
|
|
38
|
+
for (const span of spans) {
|
|
39
|
+
const scopeKey = span.instrumentationScope
|
|
40
|
+
? `${span.instrumentationScope.name}:${span.instrumentationScope.version}`
|
|
41
|
+
: 'default:1.0.0';
|
|
42
|
+
|
|
43
|
+
if (!scopeMap.has(scopeKey)) {
|
|
44
|
+
scopeMap.set(scopeKey, {
|
|
45
|
+
scope: span.instrumentationScope || {
|
|
46
|
+
name: 'default',
|
|
47
|
+
version: '1.0.0',
|
|
48
|
+
attributes: [],
|
|
49
|
+
},
|
|
50
|
+
spans: [],
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
scopeMap.get(scopeKey).spans.push(this._transformSpan(span));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
resourceSpans: [
|
|
59
|
+
{
|
|
60
|
+
resource: this._transformResource(resource),
|
|
61
|
+
scopeSpans: Array.from(scopeMap.values()).map((scopeData) => ({
|
|
62
|
+
scope: this._transformInstrumentationScope(scopeData.scope),
|
|
63
|
+
spans: scopeData.spans,
|
|
64
|
+
})),
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Transforms a ReadableSpan into the OTLP Span format
|
|
72
|
+
*
|
|
73
|
+
* @private
|
|
74
|
+
* @param {Object} span - ReadableSpan object to transform
|
|
75
|
+
* @returns {Object} OTLP Span format
|
|
76
|
+
*/
|
|
77
|
+
_transformSpan(span) {
|
|
78
|
+
const transformAttributes = (attributes) => {
|
|
79
|
+
return Object.entries(attributes || {}).map(([key, value]) => ({
|
|
80
|
+
key,
|
|
81
|
+
value: this._transformAnyValue(value),
|
|
82
|
+
}));
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const transformEvents = (events) => {
|
|
86
|
+
return (events || []).map((event) => ({
|
|
87
|
+
timeUnixNano: hrtime.toNanos(event.time),
|
|
88
|
+
name: event.name,
|
|
89
|
+
attributes: transformAttributes(event.attributes),
|
|
90
|
+
}));
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
traceId: span.spanContext.traceId,
|
|
95
|
+
spanId: span.spanContext.spanId,
|
|
96
|
+
parentSpanId: span.parentSpanId || '',
|
|
97
|
+
name: span.name,
|
|
98
|
+
kind: span.kind || 1, // INTERNAL by default
|
|
99
|
+
startTimeUnixNano: hrtime.toNanos(span.startTime),
|
|
100
|
+
endTimeUnixNano: hrtime.toNanos(span.endTime),
|
|
101
|
+
attributes: transformAttributes(span.attributes),
|
|
102
|
+
events: transformEvents(span.events),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Transforms a resource object into OTLP Resource format
|
|
108
|
+
*
|
|
109
|
+
* @private
|
|
110
|
+
* @param {Object} resource - Resource information
|
|
111
|
+
* @returns {Object} OTLP Resource format
|
|
112
|
+
*/
|
|
113
|
+
_transformResource(resource) {
|
|
114
|
+
const attributes = resource.attributes || {};
|
|
115
|
+
const keyValues = Object.entries(attributes).map(([key, value]) => ({
|
|
116
|
+
key,
|
|
117
|
+
value: this._transformAnyValue(value),
|
|
118
|
+
}));
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
attributes: keyValues,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Transforms an instrumentation scope into OTLP InstrumentationScope format
|
|
127
|
+
*
|
|
128
|
+
* @private
|
|
129
|
+
* @param {Object} scope - Instrumentation scope information
|
|
130
|
+
* @returns {Object} OTLP InstrumentationScope format
|
|
131
|
+
*/
|
|
132
|
+
_transformInstrumentationScope(scope) {
|
|
133
|
+
return {
|
|
134
|
+
name: scope.name || '',
|
|
135
|
+
version: scope.version || '',
|
|
136
|
+
attributes: (scope.attributes || []).map((attr) => ({
|
|
137
|
+
key: attr.key,
|
|
138
|
+
value: this._transformAnyValue(attr.value),
|
|
139
|
+
})),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Transforms a JavaScript value into an OTLP AnyValue
|
|
145
|
+
*
|
|
146
|
+
* @private
|
|
147
|
+
* @param {any} value - Value to transform
|
|
148
|
+
* @returns {Object} OTLP AnyValue format
|
|
149
|
+
*/
|
|
150
|
+
_transformAnyValue(value) {
|
|
151
|
+
if (value === null || value === undefined) {
|
|
152
|
+
return { stringValue: '' };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const type = typeof value;
|
|
156
|
+
|
|
157
|
+
if (type === 'string') {
|
|
158
|
+
return { stringValue: value };
|
|
159
|
+
} else if (type === 'number') {
|
|
160
|
+
if (Number.isInteger(value)) {
|
|
161
|
+
return { intValue: value.toString() };
|
|
162
|
+
} else {
|
|
163
|
+
return { doubleValue: value };
|
|
164
|
+
}
|
|
165
|
+
} else if (type === 'boolean') {
|
|
166
|
+
return { boolValue: value };
|
|
167
|
+
} else if (Array.isArray(value)) {
|
|
168
|
+
return {
|
|
169
|
+
arrayValue: {
|
|
170
|
+
values: value.map((v) => this._transformAnyValue(v)),
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
} else if (type === 'object') {
|
|
174
|
+
return {
|
|
175
|
+
kvlistValue: {
|
|
176
|
+
values: Object.entries(value).map(([k, v]) => ({
|
|
177
|
+
key: k,
|
|
178
|
+
value: this._transformAnyValue(v),
|
|
179
|
+
})),
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return { stringValue: String(value) };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export const spanExportQueue = [];
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module hrtime
|
|
3
|
+
*
|
|
4
|
+
* @description Methods for handling OpenTelemetry hrtime.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Convert a duration in milliseconds to an OpenTelemetry hrtime tuple.
|
|
9
|
+
*
|
|
10
|
+
* @param {number} millis - The duration in milliseconds.
|
|
11
|
+
* @returns {[number, number]} An array where the first element is seconds
|
|
12
|
+
* and the second is nanoseconds.
|
|
13
|
+
*/
|
|
14
|
+
function fromMillis(millis) {
|
|
15
|
+
return [Math.trunc(millis / 1000), Math.round((millis % 1000) * 1e6)];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Convert an OpenTelemetry hrtime tuple back to a duration in milliseconds.
|
|
20
|
+
*
|
|
21
|
+
* @param {[number, number]} hrtime - The hrtime tuple [seconds, nanoseconds].
|
|
22
|
+
* @returns {number} The total duration in milliseconds.
|
|
23
|
+
*/
|
|
24
|
+
function toMillis(hrtime) {
|
|
25
|
+
return hrtime[0] * 1e3 + Math.round(hrtime[1] / 1e6);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Convert an OpenTelemetry hrtime tuple back to a duration in nanoseconds.
|
|
30
|
+
*
|
|
31
|
+
* @param {[number, number]} hrtime - The hrtime tuple [seconds, nanoseconds].
|
|
32
|
+
* @returns {number} The total duration in nanoseconds.
|
|
33
|
+
*/
|
|
34
|
+
function toNanos(hrtime) {
|
|
35
|
+
return hrtime[0] * 1e9 + hrtime[1];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Adds two OpenTelemetry hrtime tuples.
|
|
40
|
+
*
|
|
41
|
+
* @param {[number, number]} a - The first hrtime tuple [s, ns].
|
|
42
|
+
* @param {[number, number]} b - The second hrtime tuple [s, ns].
|
|
43
|
+
* @returns {[number, number]} Summed hrtime tuple, normalized.
|
|
44
|
+
*
|
|
45
|
+
*/
|
|
46
|
+
function add(a, b) {
|
|
47
|
+
return [a[0] + b[0] + Math.trunc((a[1] + b[1]) / 1e9), (a[1] + b[1]) % 1e9];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get the current high-resolution time as an OpenTelemetry hrtime tuple.
|
|
52
|
+
*
|
|
53
|
+
* Uses the Performance API (timeOrigin + now()).
|
|
54
|
+
*
|
|
55
|
+
* @returns {[number, number]} The current hrtime tuple [s, ns].
|
|
56
|
+
*/
|
|
57
|
+
function now() {
|
|
58
|
+
return add(fromMillis(performance.timeOrigin), fromMillis(performance.now()));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Check if a value is a valid OpenTelemetry hrtime tuple.
|
|
63
|
+
*
|
|
64
|
+
* An hrtime tuple is an Array of exactly two numbers:
|
|
65
|
+
* [seconds, nanoseconds]
|
|
66
|
+
*
|
|
67
|
+
* @param {*} value – anything to test
|
|
68
|
+
* @returns {boolean} true if `value` is a [number, number] array of length 2
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* isHrTime([ 1, 500 ]); // true
|
|
72
|
+
* isHrTime([ 0, 1e9 ]); // true
|
|
73
|
+
* isHrTime([ '1', 500 ]); // false
|
|
74
|
+
* isHrTime({ 0: 1, 1: 500 }); // false
|
|
75
|
+
*/
|
|
76
|
+
function isHrTime(value) {
|
|
77
|
+
return (
|
|
78
|
+
Array.isArray(value) &&
|
|
79
|
+
value.length === 2 &&
|
|
80
|
+
typeof value[0] === 'number' &&
|
|
81
|
+
typeof value[1] === 'number'
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Methods for handling hrtime. OpenTelemetry uses the [seconds, nanoseconds]
|
|
87
|
+
* format for hrtime in the `ReadableSpan` interface.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* import hrtime from '@tracing/hrtime.js';
|
|
91
|
+
*
|
|
92
|
+
* hrtime.fromMillis(1000);
|
|
93
|
+
* hrtime.toMillis([0, 1000]);
|
|
94
|
+
* hrtime.add([0, 0], [0, 1000]);
|
|
95
|
+
* hrtime.now();
|
|
96
|
+
* hrtime.isHrTime([0, 1000]);
|
|
97
|
+
*/
|
|
98
|
+
export default { fromMillis, toMillis, toNanos, add, now, isHrTime };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a random hexadecimal ID of specified byte length
|
|
3
|
+
*
|
|
4
|
+
* @param {number} bytes - Number of bytes for the ID (default: 16)
|
|
5
|
+
* @returns {string} - Hexadecimal string representation
|
|
6
|
+
*/
|
|
7
|
+
function gen(bytes = 16) {
|
|
8
|
+
let randomBytes = new Uint8Array(bytes);
|
|
9
|
+
crypto.getRandomValues(randomBytes);
|
|
10
|
+
let randHex = Array.from(randomBytes, (byte) =>
|
|
11
|
+
byte.toString(16).padStart(2, '0'),
|
|
12
|
+
).join('');
|
|
13
|
+
return randHex;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Tracing id generation utils
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* import id from './id.js';
|
|
21
|
+
*
|
|
22
|
+
* const spanId = id.gen(8); // => "a1b2c3d4e5f6..."
|
|
23
|
+
*/
|
|
24
|
+
export default { gen };
|