@obtrace/browser 2.5.1 → 2.5.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/dist/browser/clicks.js +7 -0
- package/dist/browser/errors.js +2 -2
- package/dist/browser/index.js +15 -5
- package/dist/browser/replay.js +4 -4
- package/dist/browser/resources.js +25 -8
- package/dist/browser/vitals.js +7 -1
- package/dist/browser_entry.bundle.js +55 -35
- package/dist/browser_entry.bundle.js.map +2 -2
- package/package.json +1 -1
package/dist/browser/clicks.js
CHANGED
|
@@ -16,6 +16,13 @@ export function installClickTracking(tracer, sessionId) {
|
|
|
16
16
|
return;
|
|
17
17
|
const selector = getElementSelector(target);
|
|
18
18
|
const now = Date.now();
|
|
19
|
+
addBreadcrumb({
|
|
20
|
+
timestamp: now,
|
|
21
|
+
category: "ui.click",
|
|
22
|
+
message: selector,
|
|
23
|
+
level: "info",
|
|
24
|
+
data: { x: ev.clientX, y: ev.clientY },
|
|
25
|
+
});
|
|
19
26
|
recent.push({ selector, timestamp: now });
|
|
20
27
|
while (recent.length > 0 && now - recent[0].timestamp > RAGE_WINDOW_MS) {
|
|
21
28
|
recent.shift();
|
package/dist/browser/errors.js
CHANGED
|
@@ -24,7 +24,7 @@ export function installBrowserErrorHooks(tracer, logger, sessionId) {
|
|
|
24
24
|
"error.stack": stack.slice(0, 4096),
|
|
25
25
|
"error.type": errorType,
|
|
26
26
|
"breadcrumbs.count": breadcrumbs.length,
|
|
27
|
-
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-
|
|
27
|
+
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-5)),
|
|
28
28
|
...(sessionId ? { "session.id": sessionId } : {}),
|
|
29
29
|
};
|
|
30
30
|
logger.emit({
|
|
@@ -71,7 +71,7 @@ export function installBrowserErrorHooks(tracer, logger, sessionId) {
|
|
|
71
71
|
"error.stack": stack.slice(0, 4096),
|
|
72
72
|
"error.type": errorType,
|
|
73
73
|
"breadcrumbs.count": breadcrumbs.length,
|
|
74
|
-
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-
|
|
74
|
+
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-5)),
|
|
75
75
|
...(sessionId ? { "session.id": sessionId } : {}),
|
|
76
76
|
};
|
|
77
77
|
logger.emit({
|
package/dist/browser/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { setupOtelWeb } from "../core/otel-web-setup";
|
|
|
6
6
|
import { installBrowserErrorHooks } from "./errors";
|
|
7
7
|
import { BrowserReplayBuffer } from "./replay";
|
|
8
8
|
import { installWebVitals } from "./vitals";
|
|
9
|
-
import { addBreadcrumb as addCrumb, getBreadcrumbs
|
|
9
|
+
import { addBreadcrumb as addCrumb, getBreadcrumbs } from "./breadcrumbs";
|
|
10
10
|
import { installConsoleCapture } from "./console";
|
|
11
11
|
import { installClickTracking } from "./clicks";
|
|
12
12
|
import { installResourceTiming } from "./resources";
|
|
@@ -210,7 +210,6 @@ export function initBrowserSDK(config) {
|
|
|
210
210
|
if (config.vitals?.enabled !== false)
|
|
211
211
|
cleanups.push(installWebVitals(meter, !!config.vitals?.reportAllChanges));
|
|
212
212
|
cleanups.push(installBrowserErrorHooks(tracer, logger, replay.sessionId));
|
|
213
|
-
cleanups.push(installClickBreadcrumbs());
|
|
214
213
|
cleanups.push(installClickTracking(tracer, replay.sessionId));
|
|
215
214
|
cleanups.push(installResourceTiming(meter));
|
|
216
215
|
cleanups.push(installLongTaskDetection(tracer));
|
|
@@ -227,11 +226,16 @@ export function initBrowserSDK(config) {
|
|
|
227
226
|
installSharedNavigationTracker();
|
|
228
227
|
}
|
|
229
228
|
let pendingBeaconBlob = null;
|
|
229
|
+
const scheduleIdle = typeof requestIdleCallback === "function"
|
|
230
|
+
? (fn) => requestIdleCallback(fn, { timeout: 2000 })
|
|
231
|
+
: (fn) => setTimeout(fn, 0);
|
|
230
232
|
client.replayTimer = setInterval(() => {
|
|
231
233
|
const chunk = replay.flush();
|
|
232
234
|
if (chunk) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
+
scheduleIdle(() => {
|
|
236
|
+
const json = JSON.stringify(chunk);
|
|
237
|
+
pendingBeaconBlob = new Blob([json], { type: "application/json" });
|
|
238
|
+
});
|
|
235
239
|
client.replayChunk(chunk);
|
|
236
240
|
}
|
|
237
241
|
else {
|
|
@@ -303,8 +307,14 @@ export function initBrowserSDK(config) {
|
|
|
303
307
|
span.end();
|
|
304
308
|
}
|
|
305
309
|
};
|
|
310
|
+
const gaugeCache = new Map();
|
|
306
311
|
const metricFn = (name, value, unit, context) => {
|
|
307
|
-
const
|
|
312
|
+
const key = `${name}\0${unit ?? "1"}`;
|
|
313
|
+
let gauge = gaugeCache.get(key);
|
|
314
|
+
if (!gauge) {
|
|
315
|
+
gauge = meter.createGauge(name, { unit: unit ?? "1" });
|
|
316
|
+
gaugeCache.set(key, gauge);
|
|
317
|
+
}
|
|
308
318
|
gauge.record(value, { ...userAttrs(), ...context?.attrs });
|
|
309
319
|
};
|
|
310
320
|
const captureException = (error, context) => {
|
package/dist/browser/replay.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { EventType } from "@rrweb/types";
|
|
2
2
|
import { sanitizeHeaders, stripQuery, toBase64 } from "../shared/utils";
|
|
3
3
|
const KEY_OVERHEAD = 6;
|
|
4
|
-
function estimateObjectBytes(value) {
|
|
5
|
-
if (value === null || value === undefined)
|
|
4
|
+
function estimateObjectBytes(value, depth = 0) {
|
|
5
|
+
if (depth > 8 || value === null || value === undefined)
|
|
6
6
|
return 4;
|
|
7
7
|
switch (typeof value) {
|
|
8
8
|
case "string":
|
|
@@ -14,14 +14,14 @@ function estimateObjectBytes(value) {
|
|
|
14
14
|
if (Array.isArray(value)) {
|
|
15
15
|
let sum = 2;
|
|
16
16
|
for (let i = 0; i < value.length; i++) {
|
|
17
|
-
sum += estimateObjectBytes(value[i]) + 1;
|
|
17
|
+
sum += estimateObjectBytes(value[i], depth + 1) + 1;
|
|
18
18
|
}
|
|
19
19
|
return sum;
|
|
20
20
|
}
|
|
21
21
|
let sum = 2;
|
|
22
22
|
const keys = Object.keys(value);
|
|
23
23
|
for (let i = 0; i < keys.length; i++) {
|
|
24
|
-
sum += keys[i].length + KEY_OVERHEAD + estimateObjectBytes(value[keys[i]]);
|
|
24
|
+
sum += keys[i].length + KEY_OVERHEAD + estimateObjectBytes(value[keys[i]], depth + 1);
|
|
25
25
|
}
|
|
26
26
|
return sum;
|
|
27
27
|
}
|
|
@@ -2,19 +2,32 @@ export function installResourceTiming(meter) {
|
|
|
2
2
|
if (typeof window === "undefined" || typeof PerformanceObserver === "undefined")
|
|
3
3
|
return () => { };
|
|
4
4
|
const gauge = meter.createGauge("browser.resource.duration", { unit: "ms" });
|
|
5
|
+
let pendingEntries = [];
|
|
6
|
+
let flushTimer = null;
|
|
7
|
+
const flushEntries = () => {
|
|
8
|
+
flushTimer = null;
|
|
9
|
+
const batch = pendingEntries;
|
|
10
|
+
pendingEntries = [];
|
|
11
|
+
for (const res of batch) {
|
|
12
|
+
const name = typeof res.name === "string" ? res.name : "";
|
|
13
|
+
const shortName = name.split("?")[0].split("/").pop() || name.slice(0, 80) || "unknown";
|
|
14
|
+
gauge.record(res.duration, {
|
|
15
|
+
"resource.type": String(res.initiatorType || "other"),
|
|
16
|
+
"resource.name": String(shortName),
|
|
17
|
+
"resource.transfer_size": Number(res.transferSize) || 0,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
};
|
|
5
21
|
const observer = new PerformanceObserver((list) => {
|
|
6
22
|
try {
|
|
7
23
|
for (const entry of list.getEntries()) {
|
|
8
24
|
const res = entry;
|
|
9
25
|
if (res.duration < 100)
|
|
10
26
|
continue;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"resource.name": String(shortName),
|
|
16
|
-
"resource.transfer_size": Number(res.transferSize) || 0,
|
|
17
|
-
});
|
|
27
|
+
pendingEntries.push(res);
|
|
28
|
+
}
|
|
29
|
+
if (pendingEntries.length > 0 && !flushTimer) {
|
|
30
|
+
flushTimer = setTimeout(flushEntries, 1000);
|
|
18
31
|
}
|
|
19
32
|
}
|
|
20
33
|
catch { }
|
|
@@ -25,5 +38,9 @@ export function installResourceTiming(meter) {
|
|
|
25
38
|
catch {
|
|
26
39
|
return () => { };
|
|
27
40
|
}
|
|
28
|
-
return () =>
|
|
41
|
+
return () => {
|
|
42
|
+
observer.disconnect();
|
|
43
|
+
if (flushTimer)
|
|
44
|
+
clearTimeout(flushTimer);
|
|
45
|
+
};
|
|
29
46
|
}
|
package/dist/browser/vitals.js
CHANGED
|
@@ -59,7 +59,13 @@ export function installWebVitals(meter, reportAllChanges) {
|
|
|
59
59
|
if (ev.duration > existing) {
|
|
60
60
|
interactionDurations.set(ev.interactionId, ev.duration);
|
|
61
61
|
}
|
|
62
|
-
if (interactionDurations.size >
|
|
62
|
+
if (interactionDurations.size > 100) {
|
|
63
|
+
const entries = [...interactionDurations.entries()].sort((a, b) => b[1] - a[1]);
|
|
64
|
+
interactionDurations.clear();
|
|
65
|
+
for (const [k, v] of entries.slice(0, 50))
|
|
66
|
+
interactionDurations.set(k, v);
|
|
67
|
+
}
|
|
68
|
+
if (interactionDurations.size > 10) {
|
|
63
69
|
const sorted = [...interactionDurations.values()].sort((a, b) => b - a);
|
|
64
70
|
const p98Index = Math.max(0, Math.ceil(sorted.length * 0.02) - 1);
|
|
65
71
|
inpGauge.record(sorted[p98Index], { vital: "inp" });
|
|
@@ -29849,22 +29849,6 @@ function getElementSelector(el) {
|
|
|
29849
29849
|
}
|
|
29850
29850
|
return sel;
|
|
29851
29851
|
}
|
|
29852
|
-
function installClickBreadcrumbs() {
|
|
29853
|
-
if (typeof document === "undefined") return () => {
|
|
29854
|
-
};
|
|
29855
|
-
const handler = (ev) => {
|
|
29856
|
-
const target = ev.target;
|
|
29857
|
-
addBreadcrumb({
|
|
29858
|
-
timestamp: Date.now(),
|
|
29859
|
-
category: "ui.click",
|
|
29860
|
-
message: getElementSelector(target),
|
|
29861
|
-
level: "info",
|
|
29862
|
-
data: { x: ev.clientX, y: ev.clientY }
|
|
29863
|
-
});
|
|
29864
|
-
};
|
|
29865
|
-
document.addEventListener("click", handler, true);
|
|
29866
|
-
return () => document.removeEventListener("click", handler, true);
|
|
29867
|
-
}
|
|
29868
29852
|
|
|
29869
29853
|
// src/browser/errors.ts
|
|
29870
29854
|
var processing = false;
|
|
@@ -29889,7 +29873,7 @@ function installBrowserErrorHooks(tracer, logger, sessionId) {
|
|
|
29889
29873
|
"error.stack": stack.slice(0, 4096),
|
|
29890
29874
|
"error.type": errorType,
|
|
29891
29875
|
"breadcrumbs.count": breadcrumbs.length,
|
|
29892
|
-
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-
|
|
29876
|
+
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-5)),
|
|
29893
29877
|
...sessionId ? { "session.id": sessionId } : {}
|
|
29894
29878
|
};
|
|
29895
29879
|
logger.emit({
|
|
@@ -29933,7 +29917,7 @@ function installBrowserErrorHooks(tracer, logger, sessionId) {
|
|
|
29933
29917
|
"error.stack": stack.slice(0, 4096),
|
|
29934
29918
|
"error.type": errorType,
|
|
29935
29919
|
"breadcrumbs.count": breadcrumbs.length,
|
|
29936
|
-
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-
|
|
29920
|
+
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-5)),
|
|
29937
29921
|
...sessionId ? { "session.id": sessionId } : {}
|
|
29938
29922
|
};
|
|
29939
29923
|
logger.emit({
|
|
@@ -30022,8 +30006,8 @@ function stripQuery(url) {
|
|
|
30022
30006
|
|
|
30023
30007
|
// src/browser/replay.ts
|
|
30024
30008
|
var KEY_OVERHEAD = 6;
|
|
30025
|
-
function estimateObjectBytes(value) {
|
|
30026
|
-
if (value === null || value === void 0) return 4;
|
|
30009
|
+
function estimateObjectBytes(value, depth = 0) {
|
|
30010
|
+
if (depth > 8 || value === null || value === void 0) return 4;
|
|
30027
30011
|
switch (typeof value) {
|
|
30028
30012
|
case "string":
|
|
30029
30013
|
return value.length + 2;
|
|
@@ -30034,14 +30018,14 @@ function estimateObjectBytes(value) {
|
|
|
30034
30018
|
if (Array.isArray(value)) {
|
|
30035
30019
|
let sum2 = 2;
|
|
30036
30020
|
for (let i = 0; i < value.length; i++) {
|
|
30037
|
-
sum2 += estimateObjectBytes(value[i]) + 1;
|
|
30021
|
+
sum2 += estimateObjectBytes(value[i], depth + 1) + 1;
|
|
30038
30022
|
}
|
|
30039
30023
|
return sum2;
|
|
30040
30024
|
}
|
|
30041
30025
|
let sum = 2;
|
|
30042
30026
|
const keys = Object.keys(value);
|
|
30043
30027
|
for (let i = 0; i < keys.length; i++) {
|
|
30044
|
-
sum += keys[i].length + KEY_OVERHEAD + estimateObjectBytes(value[keys[i]]);
|
|
30028
|
+
sum += keys[i].length + KEY_OVERHEAD + estimateObjectBytes(value[keys[i]], depth + 1);
|
|
30045
30029
|
}
|
|
30046
30030
|
return sum;
|
|
30047
30031
|
}
|
|
@@ -30202,7 +30186,12 @@ function installWebVitals(meter, reportAllChanges) {
|
|
|
30202
30186
|
if (ev.duration > existing) {
|
|
30203
30187
|
interactionDurations.set(ev.interactionId, ev.duration);
|
|
30204
30188
|
}
|
|
30205
|
-
if (interactionDurations.size >
|
|
30189
|
+
if (interactionDurations.size > 100) {
|
|
30190
|
+
const entries = [...interactionDurations.entries()].sort((a, b) => b[1] - a[1]);
|
|
30191
|
+
interactionDurations.clear();
|
|
30192
|
+
for (const [k, v] of entries.slice(0, 50)) interactionDurations.set(k, v);
|
|
30193
|
+
}
|
|
30194
|
+
if (interactionDurations.size > 10) {
|
|
30206
30195
|
const sorted = [...interactionDurations.values()].sort((a, b) => b - a);
|
|
30207
30196
|
const p98Index = Math.max(0, Math.ceil(sorted.length * 0.02) - 1);
|
|
30208
30197
|
inpGauge.record(sorted[p98Index], { vital: "inp" });
|
|
@@ -30355,6 +30344,13 @@ function installClickTracking(tracer, sessionId) {
|
|
|
30355
30344
|
if (!target) return;
|
|
30356
30345
|
const selector = getElementSelector(target);
|
|
30357
30346
|
const now = Date.now();
|
|
30347
|
+
addBreadcrumb({
|
|
30348
|
+
timestamp: now,
|
|
30349
|
+
category: "ui.click",
|
|
30350
|
+
message: selector,
|
|
30351
|
+
level: "info",
|
|
30352
|
+
data: { x: ev.clientX, y: ev.clientY }
|
|
30353
|
+
});
|
|
30358
30354
|
recent.push({ selector, timestamp: now });
|
|
30359
30355
|
while (recent.length > 0 && now - recent[0].timestamp > RAGE_WINDOW_MS) {
|
|
30360
30356
|
recent.shift();
|
|
@@ -30405,18 +30401,31 @@ function installResourceTiming(meter) {
|
|
|
30405
30401
|
if (typeof window === "undefined" || typeof PerformanceObserver === "undefined") return () => {
|
|
30406
30402
|
};
|
|
30407
30403
|
const gauge = meter.createGauge("browser.resource.duration", { unit: "ms" });
|
|
30404
|
+
let pendingEntries = [];
|
|
30405
|
+
let flushTimer = null;
|
|
30406
|
+
const flushEntries = () => {
|
|
30407
|
+
flushTimer = null;
|
|
30408
|
+
const batch = pendingEntries;
|
|
30409
|
+
pendingEntries = [];
|
|
30410
|
+
for (const res of batch) {
|
|
30411
|
+
const name = typeof res.name === "string" ? res.name : "";
|
|
30412
|
+
const shortName = name.split("?")[0].split("/").pop() || name.slice(0, 80) || "unknown";
|
|
30413
|
+
gauge.record(res.duration, {
|
|
30414
|
+
"resource.type": String(res.initiatorType || "other"),
|
|
30415
|
+
"resource.name": String(shortName),
|
|
30416
|
+
"resource.transfer_size": Number(res.transferSize) || 0
|
|
30417
|
+
});
|
|
30418
|
+
}
|
|
30419
|
+
};
|
|
30408
30420
|
const observer = new PerformanceObserver((list2) => {
|
|
30409
30421
|
try {
|
|
30410
30422
|
for (const entry of list2.getEntries()) {
|
|
30411
30423
|
const res = entry;
|
|
30412
30424
|
if (res.duration < 100) continue;
|
|
30413
|
-
|
|
30414
|
-
|
|
30415
|
-
|
|
30416
|
-
|
|
30417
|
-
"resource.name": String(shortName),
|
|
30418
|
-
"resource.transfer_size": Number(res.transferSize) || 0
|
|
30419
|
-
});
|
|
30425
|
+
pendingEntries.push(res);
|
|
30426
|
+
}
|
|
30427
|
+
if (pendingEntries.length > 0 && !flushTimer) {
|
|
30428
|
+
flushTimer = setTimeout(flushEntries, 1e3);
|
|
30420
30429
|
}
|
|
30421
30430
|
} catch {
|
|
30422
30431
|
}
|
|
@@ -30427,7 +30436,10 @@ function installResourceTiming(meter) {
|
|
|
30427
30436
|
return () => {
|
|
30428
30437
|
};
|
|
30429
30438
|
}
|
|
30430
|
-
return () =>
|
|
30439
|
+
return () => {
|
|
30440
|
+
observer.disconnect();
|
|
30441
|
+
if (flushTimer) clearTimeout(flushTimer);
|
|
30442
|
+
};
|
|
30431
30443
|
}
|
|
30432
30444
|
|
|
30433
30445
|
// src/browser/longtasks.ts
|
|
@@ -30647,7 +30659,6 @@ function initBrowserSDK(config) {
|
|
|
30647
30659
|
});
|
|
30648
30660
|
if (config.vitals?.enabled !== false) cleanups.push(installWebVitals(meter, !!config.vitals?.reportAllChanges));
|
|
30649
30661
|
cleanups.push(installBrowserErrorHooks(tracer, logger, replay.sessionId));
|
|
30650
|
-
cleanups.push(installClickBreadcrumbs());
|
|
30651
30662
|
cleanups.push(installClickTracking(tracer, replay.sessionId));
|
|
30652
30663
|
cleanups.push(installResourceTiming(meter));
|
|
30653
30664
|
cleanups.push(installLongTaskDetection(tracer));
|
|
@@ -30661,11 +30672,14 @@ function initBrowserSDK(config) {
|
|
|
30661
30672
|
installSharedNavigationTracker();
|
|
30662
30673
|
}
|
|
30663
30674
|
let pendingBeaconBlob = null;
|
|
30675
|
+
const scheduleIdle = typeof requestIdleCallback === "function" ? (fn) => requestIdleCallback(fn, { timeout: 2e3 }) : (fn) => setTimeout(fn, 0);
|
|
30664
30676
|
client.replayTimer = setInterval(() => {
|
|
30665
30677
|
const chunk = replay.flush();
|
|
30666
30678
|
if (chunk) {
|
|
30667
|
-
|
|
30668
|
-
|
|
30679
|
+
scheduleIdle(() => {
|
|
30680
|
+
const json = JSON.stringify(chunk);
|
|
30681
|
+
pendingBeaconBlob = new Blob([json], { type: "application/json" });
|
|
30682
|
+
});
|
|
30669
30683
|
client.replayChunk(chunk);
|
|
30670
30684
|
} else {
|
|
30671
30685
|
pendingBeaconBlob = null;
|
|
@@ -30735,8 +30749,14 @@ function initBrowserSDK(config) {
|
|
|
30735
30749
|
span.end();
|
|
30736
30750
|
}
|
|
30737
30751
|
};
|
|
30752
|
+
const gaugeCache = /* @__PURE__ */ new Map();
|
|
30738
30753
|
const metricFn = (name, value, unit, context2) => {
|
|
30739
|
-
const
|
|
30754
|
+
const key = `${name}\0${unit ?? "1"}`;
|
|
30755
|
+
let gauge = gaugeCache.get(key);
|
|
30756
|
+
if (!gauge) {
|
|
30757
|
+
gauge = meter.createGauge(name, { unit: unit ?? "1" });
|
|
30758
|
+
gaugeCache.set(key, gauge);
|
|
30759
|
+
}
|
|
30740
30760
|
gauge.record(value, { ...userAttrs(), ...context2?.attrs });
|
|
30741
30761
|
};
|
|
30742
30762
|
const captureException = (error, context2) => {
|