rollbar 3.0.0 → 3.1.0
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/dist/rollbar.cjs +260 -3
- package/dist/rollbar.js +88 -2
- package/dist/rollbar.js.map +1 -1
- package/dist/rollbar.min.cjs +1 -1
- package/dist/rollbar.min.js +1 -1
- package/dist/rollbar.min.js.map +1 -1
- package/dist/rollbar.named-amd.js +88 -2
- package/dist/rollbar.named-amd.js.map +1 -1
- package/dist/rollbar.named-amd.min.js +1 -1
- package/dist/rollbar.named-amd.min.js.map +1 -1
- package/dist/rollbar.noconflict.umd.js +88 -2
- package/dist/rollbar.noconflict.umd.js.map +1 -1
- package/dist/rollbar.noconflict.umd.min.js +1 -1
- package/dist/rollbar.noconflict.umd.min.js.map +1 -1
- package/dist/rollbar.replay.js +88 -2
- package/dist/rollbar.replay.js.map +1 -1
- package/dist/rollbar.replay.min.js +1 -1
- package/dist/rollbar.replay.min.js.map +1 -1
- package/dist/rollbar.replay.noconflict.umd.js +88 -2
- package/dist/rollbar.replay.noconflict.umd.js.map +1 -1
- package/dist/rollbar.replay.noconflict.umd.min.js +1 -1
- package/dist/rollbar.replay.noconflict.umd.min.js.map +1 -1
- package/dist/rollbar.replay.umd.js +88 -2
- package/dist/rollbar.replay.umd.js.map +1 -1
- package/dist/rollbar.replay.umd.min.js +1 -1
- package/dist/rollbar.replay.umd.min.js.map +1 -1
- package/dist/rollbar.snippet.js +1 -1
- package/dist/rollbar.umd.js +88 -2
- package/dist/rollbar.umd.js.map +1 -1
- package/dist/rollbar.umd.min.js +1 -1
- package/dist/rollbar.umd.min.js.map +1 -1
- package/index.d.ts +15 -0
- package/package.json +2 -1
- package/src/browser/telemetry.js +25 -0
- package/src/defaults.js +1 -1
- package/src/rollbar.js +3 -1
- package/src/server/middleware/rollbarExpressMiddleware.js +63 -0
- package/src/server/rollbar.js +13 -0
- package/src/server/telemetry.js +120 -1
- package/src/utility.js +70 -0
package/index.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ declare class Rollbar implements Rollbar.Components {
|
|
|
33
33
|
): Rollbar.LambdaHandler<T>;
|
|
34
34
|
|
|
35
35
|
public errorHandler(): Rollbar.ExpressErrorHandler;
|
|
36
|
+
public expressMiddleware(): Rollbar.ExpressMiddleware | undefined;
|
|
36
37
|
|
|
37
38
|
// Components
|
|
38
39
|
|
|
@@ -219,6 +220,11 @@ declare namespace Rollbar {
|
|
|
219
220
|
response: any,
|
|
220
221
|
next: ExpressNextFunction,
|
|
221
222
|
) => any;
|
|
223
|
+
export type ExpressMiddleware = (
|
|
224
|
+
request: any,
|
|
225
|
+
response: any,
|
|
226
|
+
next: ExpressNextFunction,
|
|
227
|
+
) => any;
|
|
222
228
|
export type ExpressNextFunction = (err?: any) => void;
|
|
223
229
|
class Locals {}
|
|
224
230
|
export type LocalsType = typeof Locals;
|
|
@@ -333,9 +339,18 @@ declare namespace Rollbar {
|
|
|
333
339
|
export interface TransformSpanParams {
|
|
334
340
|
span: any;
|
|
335
341
|
}
|
|
342
|
+
export type TracingPropagationHeader =
|
|
343
|
+
| 'baggage'
|
|
344
|
+
| 'traceparent'
|
|
345
|
+
| 'tracestate';
|
|
346
|
+
export interface TracingPropagationOptions {
|
|
347
|
+
enabledHeaders?: TracingPropagationHeader[];
|
|
348
|
+
enabledCorsUrls?: (string | RegExp)[];
|
|
349
|
+
}
|
|
336
350
|
export interface TracingOptions {
|
|
337
351
|
enabled?: boolean;
|
|
338
352
|
endpoint?: string;
|
|
353
|
+
propagation?: TracingPropagationOptions;
|
|
339
354
|
transformSpan?: (params: TransformSpanParams) => void;
|
|
340
355
|
}
|
|
341
356
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rollbar",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -125,6 +125,7 @@
|
|
|
125
125
|
"ts-node": "^10.9.2",
|
|
126
126
|
"typescript": "^5.9.3",
|
|
127
127
|
"typescript-eslint": "^8.46.4",
|
|
128
|
+
"undici": "^6.23.0",
|
|
128
129
|
"webpack": "^5.98.0",
|
|
129
130
|
"webpack-cli": "^6.0.1",
|
|
130
131
|
"webpack-node-externals": "^3.0.0"
|
package/src/browser/telemetry.js
CHANGED
|
@@ -284,6 +284,23 @@ class Instrumenter {
|
|
|
284
284
|
function (orig) {
|
|
285
285
|
return function (data) {
|
|
286
286
|
const xhr = this;
|
|
287
|
+
const tracing = self.rollbar?.tracing;
|
|
288
|
+
if (
|
|
289
|
+
_.shouldAddBaggageHeader(
|
|
290
|
+
self.options,
|
|
291
|
+
tracing,
|
|
292
|
+
xhr.__rollbar_xhr?.url,
|
|
293
|
+
)
|
|
294
|
+
) {
|
|
295
|
+
try {
|
|
296
|
+
xhr.setRequestHeader(
|
|
297
|
+
'baggage',
|
|
298
|
+
`rollbar.session.id=${tracing.sessionId}`,
|
|
299
|
+
);
|
|
300
|
+
} catch (_e) {
|
|
301
|
+
/* ignore errors from adding baggage header */
|
|
302
|
+
}
|
|
303
|
+
}
|
|
287
304
|
|
|
288
305
|
function onreadystatechangeHandler() {
|
|
289
306
|
if (xhr.__rollbar_xhr) {
|
|
@@ -435,6 +452,14 @@ class Instrumenter {
|
|
|
435
452
|
if (args[1] && args[1].method) {
|
|
436
453
|
method = args[1].method;
|
|
437
454
|
}
|
|
455
|
+
const tracing = self.rollbar?.tracing;
|
|
456
|
+
if (_.shouldAddBaggageHeader(self.options, tracing, url)) {
|
|
457
|
+
const headers = {
|
|
458
|
+
baggage: `rollbar.session.id=${tracing.sessionId}`,
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
_.addHeadersToFetch(args, headers);
|
|
462
|
+
}
|
|
438
463
|
const metadata = {
|
|
439
464
|
method: method,
|
|
440
465
|
url: url,
|
package/src/defaults.js
CHANGED
package/src/rollbar.js
CHANGED
|
@@ -184,9 +184,11 @@ Rollbar.prototype._log = function (defaultLevel, item) {
|
|
|
184
184
|
|
|
185
185
|
Rollbar.prototype._addItemAttributes = function (item) {
|
|
186
186
|
const span = this.tracing?.getSpan();
|
|
187
|
+
const asyncLocalSessionId = _.getSessionIdFromAsyncLocalStorage(this);
|
|
188
|
+
const sessionId = asyncLocalSessionId || this.tracing?.sessionId;
|
|
187
189
|
|
|
188
190
|
const attributes = [
|
|
189
|
-
{ key: 'session_id', value:
|
|
191
|
+
{ key: 'session_id', value: sessionId },
|
|
190
192
|
{ key: 'span_id', value: span?.spanId },
|
|
191
193
|
{ key: 'trace_id', value: span?.traceId },
|
|
192
194
|
];
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
|
|
3
|
+
export function extractSessionId(headerValue) {
|
|
4
|
+
if (!headerValue) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
const rawValue = Array.isArray(headerValue)
|
|
8
|
+
? headerValue.join(',')
|
|
9
|
+
: headerValue;
|
|
10
|
+
if (typeof rawValue !== 'string') {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const entries = rawValue.split(',');
|
|
14
|
+
for (const entry of entries) {
|
|
15
|
+
const trimmed = entry.trim();
|
|
16
|
+
if (!trimmed) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const equalsIndex = trimmed.indexOf('=');
|
|
20
|
+
if (equalsIndex === -1) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const key = trimmed.slice(0, equalsIndex).trim();
|
|
24
|
+
if (key !== 'rollbar.session.id') {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const value = trimmed.slice(equalsIndex + 1).trim();
|
|
28
|
+
if (!value) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
return decodeURIComponent(value);
|
|
33
|
+
} catch (_e) {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getBaggageHeader(req) {
|
|
41
|
+
if (!req) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (typeof req.get === 'function') {
|
|
45
|
+
return req.get('baggage');
|
|
46
|
+
}
|
|
47
|
+
return req.headers?.baggage || null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default function rollbarExpressMiddleware(rollbar) {
|
|
51
|
+
const storage = rollbar?.client.asyncLocalStorage || new AsyncLocalStorage();
|
|
52
|
+
if (rollbar) {
|
|
53
|
+
rollbar.client.asyncLocalStorage = storage;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return function rollbarExpressMiddlewareHandler(req, _res, next) {
|
|
57
|
+
const sessionId = extractSessionId(getBaggageHeader(req));
|
|
58
|
+
if (!sessionId) {
|
|
59
|
+
return next();
|
|
60
|
+
}
|
|
61
|
+
return storage.run({ sessionId }, () => next());
|
|
62
|
+
};
|
|
63
|
+
}
|
package/src/server/rollbar.js
CHANGED
|
@@ -15,6 +15,7 @@ import truncation from '../truncation.js';
|
|
|
15
15
|
import * as _ from '../utility.js';
|
|
16
16
|
|
|
17
17
|
import * as serverDefaults from './defaults.js';
|
|
18
|
+
import rollbarExpressMiddleware from './middleware/rollbarExpressMiddleware.js';
|
|
18
19
|
import Instrumenter from './telemetry.js';
|
|
19
20
|
import * as transforms from './transforms.js';
|
|
20
21
|
import Transport from './transport.js';
|
|
@@ -460,6 +461,18 @@ Rollbar.wrapCallback = function (f) {
|
|
|
460
461
|
}
|
|
461
462
|
};
|
|
462
463
|
|
|
464
|
+
Rollbar.prototype.expressMiddleware = function () {
|
|
465
|
+
return rollbarExpressMiddleware(this);
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
Rollbar.expressMiddleware = function () {
|
|
469
|
+
if (_instance) {
|
|
470
|
+
return rollbarExpressMiddleware(_instance);
|
|
471
|
+
}
|
|
472
|
+
handleUninitialized();
|
|
473
|
+
return undefined;
|
|
474
|
+
};
|
|
475
|
+
|
|
463
476
|
Rollbar.prototype.captureEvent = function () {
|
|
464
477
|
var event = _.createTelemetryEvent(arguments);
|
|
465
478
|
return this.client.captureEvent(event.type, event.metadata, event.level);
|
package/src/server/telemetry.js
CHANGED
|
@@ -85,6 +85,15 @@ Instrumenter.prototype.instrumentNetwork = function () {
|
|
|
85
85
|
this.replacements,
|
|
86
86
|
'network',
|
|
87
87
|
);
|
|
88
|
+
if (typeof globalThis.fetch === 'function') {
|
|
89
|
+
replace(
|
|
90
|
+
globalThis,
|
|
91
|
+
'fetch',
|
|
92
|
+
fetchRequestWrapper.bind(this),
|
|
93
|
+
this.replacements,
|
|
94
|
+
'network',
|
|
95
|
+
);
|
|
96
|
+
}
|
|
88
97
|
};
|
|
89
98
|
|
|
90
99
|
function networkRequestWrapper(orig) {
|
|
@@ -93,10 +102,22 @@ function networkRequestWrapper(orig) {
|
|
|
93
102
|
return (...args) => {
|
|
94
103
|
const [url, options, cb] = args;
|
|
95
104
|
var mergedOptions = urlHelpers.mergeOptions(url, options, cb);
|
|
105
|
+
const requestUrl = urlHelpers.constructUrl(mergedOptions.options);
|
|
106
|
+
const sessionId = _.getSessionIdFromAsyncLocalStorage(this.rollbar.client);
|
|
107
|
+
|
|
108
|
+
if (
|
|
109
|
+
sessionId &&
|
|
110
|
+
_.shouldAddBaggageHeader(this.options, { sessionId }, requestUrl)
|
|
111
|
+
) {
|
|
112
|
+
if (!mergedOptions.options.headers) {
|
|
113
|
+
mergedOptions.options.headers = {};
|
|
114
|
+
}
|
|
115
|
+
mergedOptions.options.headers.baggage = `rollbar.session.id=${sessionId}`;
|
|
116
|
+
}
|
|
96
117
|
|
|
97
118
|
var metadata = {
|
|
98
119
|
method: mergedOptions.options.method || 'GET',
|
|
99
|
-
url:
|
|
120
|
+
url: requestUrl,
|
|
100
121
|
status_code: null,
|
|
101
122
|
start_time_ms: _.now(),
|
|
102
123
|
end_time_ms: null,
|
|
@@ -142,6 +163,104 @@ function responseCallbackWrapper(options, metadata, callback) {
|
|
|
142
163
|
};
|
|
143
164
|
}
|
|
144
165
|
|
|
166
|
+
function fetchRequestWrapper(orig) {
|
|
167
|
+
var telemeter = this.telemeter;
|
|
168
|
+
|
|
169
|
+
return (...args) => {
|
|
170
|
+
const input = args[0];
|
|
171
|
+
const init = args[1];
|
|
172
|
+
let method = 'GET';
|
|
173
|
+
let url;
|
|
174
|
+
const sessionId = _.getSessionIdFromAsyncLocalStorage(this.rollbar.client);
|
|
175
|
+
|
|
176
|
+
if (_.isType(input, 'string') || input instanceof URL) {
|
|
177
|
+
url = input.toString();
|
|
178
|
+
} else if (input) {
|
|
179
|
+
url = input.url;
|
|
180
|
+
if (input.method) {
|
|
181
|
+
method = input.method;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (init && init.method) {
|
|
186
|
+
method = init.method;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (
|
|
190
|
+
sessionId &&
|
|
191
|
+
_.shouldAddBaggageHeader(this.options, { sessionId }, url)
|
|
192
|
+
) {
|
|
193
|
+
const headers = { baggage: `rollbar.session.id=${sessionId}` };
|
|
194
|
+
|
|
195
|
+
_.addHeadersToFetch(args, headers);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const metadata = {
|
|
199
|
+
method: method,
|
|
200
|
+
url: url,
|
|
201
|
+
status_code: null,
|
|
202
|
+
start_time_ms: _.now(),
|
|
203
|
+
end_time_ms: null,
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
if (this.autoInstrument.networkRequestHeaders) {
|
|
207
|
+
const requestHeaders = normalizeFetchHeaders(
|
|
208
|
+
init && init.headers ? init.headers : input && input.headers,
|
|
209
|
+
);
|
|
210
|
+
if (requestHeaders) {
|
|
211
|
+
metadata.request_headers = requestHeaders;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
telemeter.captureNetwork(metadata, 'fetch');
|
|
216
|
+
|
|
217
|
+
return orig.apply(globalThis, args).then(
|
|
218
|
+
(res) => {
|
|
219
|
+
metadata.end_time_ms = _.now();
|
|
220
|
+
metadata.status_code = res.status;
|
|
221
|
+
if (this.autoInstrument.networkResponseHeaders) {
|
|
222
|
+
const responseHeaders = normalizeFetchHeaders(res.headers);
|
|
223
|
+
if (responseHeaders) {
|
|
224
|
+
metadata.response = metadata.response || {};
|
|
225
|
+
metadata.response.headers = responseHeaders;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return res;
|
|
229
|
+
},
|
|
230
|
+
(err) => {
|
|
231
|
+
metadata.end_time_ms = _.now();
|
|
232
|
+
metadata.status_code = 0;
|
|
233
|
+
metadata.error = [err.name, err.message].join(': ');
|
|
234
|
+
throw err;
|
|
235
|
+
},
|
|
236
|
+
);
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function normalizeFetchHeaders(headers) {
|
|
241
|
+
if (!headers) return null;
|
|
242
|
+
if (typeof headers.forEach === 'function') {
|
|
243
|
+
const normalized = {};
|
|
244
|
+
headers.forEach((value, key) => {
|
|
245
|
+
normalized[key] = value;
|
|
246
|
+
});
|
|
247
|
+
return normalized;
|
|
248
|
+
}
|
|
249
|
+
if (Array.isArray(headers)) {
|
|
250
|
+
const normalized = {};
|
|
251
|
+
headers.forEach((pair) => {
|
|
252
|
+
if (pair && pair.length > 1) {
|
|
253
|
+
normalized[pair[0]] = pair[1];
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
return normalized;
|
|
257
|
+
}
|
|
258
|
+
if (_.isType(headers, 'object')) {
|
|
259
|
+
return headers;
|
|
260
|
+
}
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
|
|
145
264
|
Instrumenter.prototype.captureNetwork = function (
|
|
146
265
|
metadata,
|
|
147
266
|
subtype,
|
package/src/utility.js
CHANGED
|
@@ -146,6 +146,10 @@ function isBrowser() {
|
|
|
146
146
|
return typeof window !== 'undefined';
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
function isRequestObject(input) {
|
|
150
|
+
return typeof Request !== 'undefined' && input instanceof Request;
|
|
151
|
+
}
|
|
152
|
+
|
|
149
153
|
function redact() {
|
|
150
154
|
return '********';
|
|
151
155
|
}
|
|
@@ -867,6 +871,69 @@ function merge() {
|
|
|
867
871
|
return result;
|
|
868
872
|
}
|
|
869
873
|
|
|
874
|
+
function shouldAddBaggageHeader(options, tracing, url) {
|
|
875
|
+
if (!tracing?.sessionId || !url) {
|
|
876
|
+
return false;
|
|
877
|
+
}
|
|
878
|
+
const propagation = options?.tracing?.propagation;
|
|
879
|
+
const enabledHeaders = propagation?.enabledHeaders;
|
|
880
|
+
if (!Array.isArray(enabledHeaders) || !enabledHeaders.includes('baggage')) {
|
|
881
|
+
return false;
|
|
882
|
+
}
|
|
883
|
+
const enabledCorsUrls = propagation?.enabledCorsUrls;
|
|
884
|
+
if (!Array.isArray(enabledCorsUrls) || enabledCorsUrls.length === 0) {
|
|
885
|
+
return false;
|
|
886
|
+
}
|
|
887
|
+
return enabledCorsUrls.some((pattern) => {
|
|
888
|
+
if (isType(pattern, 'string')) {
|
|
889
|
+
return url === pattern;
|
|
890
|
+
}
|
|
891
|
+
if (isType(pattern, 'regexp')) {
|
|
892
|
+
return pattern.test(url);
|
|
893
|
+
}
|
|
894
|
+
return false;
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
function addHeadersToFetch(args, newHeaders) {
|
|
899
|
+
// Headers may be in the request object or the init object.
|
|
900
|
+
// If present in both places, the init object must be used.
|
|
901
|
+
//
|
|
902
|
+
let init = args[1];
|
|
903
|
+
const initHeaders = init?.headers;
|
|
904
|
+
const reqHeaders = isRequestObject(args[0]) && args[0].headers;
|
|
905
|
+
let headers = initHeaders || reqHeaders;
|
|
906
|
+
|
|
907
|
+
// If headers are not present in either place, they are added to the init object.
|
|
908
|
+
// If there is no init object, one must be created and added to args.
|
|
909
|
+
if (!headers) {
|
|
910
|
+
if (!init) {
|
|
911
|
+
args[1] = init = {};
|
|
912
|
+
}
|
|
913
|
+
headers = init.headers = {};
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// `headers` may be a Headers object or a plain object.
|
|
917
|
+
if (headers instanceof Headers) {
|
|
918
|
+
for (const key of Object.keys(newHeaders)) {
|
|
919
|
+
headers.append(key, newHeaders[key]);
|
|
920
|
+
}
|
|
921
|
+
} else if (isObject(headers)) {
|
|
922
|
+
for (const key of Object.keys(newHeaders)) {
|
|
923
|
+
headers[key] = newHeaders[key];
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
function getSessionIdFromAsyncLocalStorage(client) {
|
|
929
|
+
const storage = client.asyncLocalStorage;
|
|
930
|
+
if (!storage || typeof storage.getStore !== 'function') {
|
|
931
|
+
return null;
|
|
932
|
+
}
|
|
933
|
+
const store = storage.getStore();
|
|
934
|
+
return store?.sessionId || null;
|
|
935
|
+
}
|
|
936
|
+
|
|
870
937
|
export {
|
|
871
938
|
addParamsAndAccessTokenToPath,
|
|
872
939
|
createItem,
|
|
@@ -902,4 +969,7 @@ export {
|
|
|
902
969
|
maxByteSize,
|
|
903
970
|
typeName,
|
|
904
971
|
uuid4,
|
|
972
|
+
shouldAddBaggageHeader,
|
|
973
|
+
addHeadersToFetch,
|
|
974
|
+
getSessionIdFromAsyncLocalStorage,
|
|
905
975
|
};
|