@obtrace/browser 2.0.0 → 2.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/README.md +6 -4
- package/dist/auto.js +3 -7
- package/dist/browser/console.js +34 -23
- package/dist/browser/errors.js +35 -29
- package/dist/browser/index.js +10 -3
- package/dist/browser_entry.bundle.js +86 -53
- package/dist/browser_entry.bundle.js.map +2 -2
- package/dist/core/otel-web-setup.js +14 -3
- package/dist/types/core/otel-web-setup.d.ts +1 -0
- package/dist/wrappers/frontend/vite.js +2 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,17 +14,19 @@ bun add @obtrace/sdk-browser
|
|
|
14
14
|
|
|
15
15
|
### Simplified setup
|
|
16
16
|
|
|
17
|
-
The API key resolves `tenant_id`, `project_id`, `app_id`, and `env` automatically on the server side,
|
|
17
|
+
The API key resolves `tenant_id`, `project_id`, `app_id`, and `env` automatically on the server side. `serviceName` must match the connected app name in the project, or an explicit alias configured for that app.
|
|
18
18
|
|
|
19
19
|
```ts
|
|
20
20
|
import { initBrowserSDK } from "@obtrace/sdk-browser/browser";
|
|
21
21
|
|
|
22
22
|
const sdk = initBrowserSDK({
|
|
23
23
|
apiKey: "obt_live_...",
|
|
24
|
-
serviceName: "
|
|
24
|
+
serviceName: "core",
|
|
25
25
|
});
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
If the project app is `core` and the dashboard defines `web` as an alias, `serviceName: "web"` is accepted and normalized to `core` on ingest. Arbitrary names are rejected.
|
|
29
|
+
|
|
28
30
|
### Full configuration
|
|
29
31
|
|
|
30
32
|
For advanced use cases you can override the resolved values explicitly:
|
|
@@ -34,10 +36,10 @@ import { initBrowserSDK, SemanticMetrics } from "@obtrace/sdk-browser/browser";
|
|
|
34
36
|
|
|
35
37
|
const sdk = initBrowserSDK({
|
|
36
38
|
apiKey: "<API_KEY>",
|
|
37
|
-
serviceName: "
|
|
39
|
+
serviceName: "core",
|
|
38
40
|
tenantId: "tenant-prod",
|
|
39
41
|
projectId: "project-prod",
|
|
40
|
-
appId: "
|
|
42
|
+
appId: "core",
|
|
41
43
|
env: "prod"
|
|
42
44
|
});
|
|
43
45
|
|
package/dist/auto.js
CHANGED
|
@@ -45,14 +45,10 @@ function readEnvConfig() {
|
|
|
45
45
|
let appId;
|
|
46
46
|
let env;
|
|
47
47
|
for (const p of prefixes) {
|
|
48
|
-
apiKey = apiKey || resolveEnvVar(`${p}OBTRACE_API_KEY`);
|
|
49
|
-
ingestBaseUrl = ingestBaseUrl || resolveEnvVar(`${p}OBTRACE_INGEST_BASE_URL`);
|
|
48
|
+
apiKey = apiKey || resolveEnvVar(`${p}OBTRACE_PUBLIC_KEY`) || resolveEnvVar(`${p}OBTRACE_API_KEY`);
|
|
50
49
|
serviceName = serviceName || resolveEnvVar(`${p}OBTRACE_SERVICE_NAME`);
|
|
51
|
-
appId = appId || resolveEnvVar(`${p}OBTRACE_APP_ID`);
|
|
52
|
-
env = env || resolveEnvVar(`${p}OBTRACE_ENV`);
|
|
53
50
|
}
|
|
54
|
-
|
|
55
|
-
return { apiKey, ingestBaseUrl, serviceName, appId, env };
|
|
51
|
+
return { apiKey, serviceName };
|
|
56
52
|
}
|
|
57
53
|
function merge(...sources) {
|
|
58
54
|
const merged = {};
|
|
@@ -116,7 +112,7 @@ function autoInit() {
|
|
|
116
112
|
return;
|
|
117
113
|
}
|
|
118
114
|
console.warn("[obtrace] No configuration found. Provide config via window.__OBTRACE_CONFIG__, " +
|
|
119
|
-
'<meta name="obtrace-api-key">, or
|
|
115
|
+
'<meta name="obtrace-api-key">, or VITE_OBTRACE_PUBLIC_KEY env var. ' +
|
|
120
116
|
"Deferring initialization until config appears.");
|
|
121
117
|
setupDeferredInit();
|
|
122
118
|
}
|
package/dist/browser/console.js
CHANGED
|
@@ -20,32 +20,43 @@ export function installConsoleCapture(tracer, sessionId) {
|
|
|
20
20
|
const level = LEVEL_MAP[method];
|
|
21
21
|
console[method] = (...args) => {
|
|
22
22
|
original(...args);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
try {
|
|
24
|
+
let message;
|
|
25
|
+
let attrs = {};
|
|
26
|
+
const safeStringify = (v) => {
|
|
27
|
+
try {
|
|
28
|
+
return JSON.stringify(v);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return String(v);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
if (args.length === 1 && typeof args[0] === "object" && args[0] !== null && !Array.isArray(args[0])) {
|
|
35
|
+
const obj = args[0];
|
|
36
|
+
message = String(obj.msg || obj.message || safeStringify(obj));
|
|
37
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
38
|
+
if (k === "msg" || k === "message")
|
|
39
|
+
continue;
|
|
40
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
41
|
+
attrs[k] = v;
|
|
42
|
+
}
|
|
33
43
|
}
|
|
34
44
|
}
|
|
45
|
+
else {
|
|
46
|
+
message = args.map(a => typeof a === "string" ? a : safeStringify(a)).join(" ");
|
|
47
|
+
}
|
|
48
|
+
addBreadcrumb({ timestamp: Date.now(), category: `console.${method}`, message, level, data: attrs });
|
|
49
|
+
const span = tracer.startSpan("browser.console", {
|
|
50
|
+
attributes: {
|
|
51
|
+
"log.severity": level.toUpperCase(),
|
|
52
|
+
"log.message": message.slice(0, 1024),
|
|
53
|
+
"session.id": sessionId,
|
|
54
|
+
...attrs,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
span.end();
|
|
35
58
|
}
|
|
36
|
-
|
|
37
|
-
message = args.map(a => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
|
|
38
|
-
}
|
|
39
|
-
addBreadcrumb({ timestamp: Date.now(), category: `console.${method}`, message, level, data: attrs });
|
|
40
|
-
const span = tracer.startSpan("browser.console", {
|
|
41
|
-
attributes: {
|
|
42
|
-
"log.severity": level.toUpperCase(),
|
|
43
|
-
"log.message": message.slice(0, 1024),
|
|
44
|
-
"session.id": sessionId,
|
|
45
|
-
...attrs,
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
span.end();
|
|
59
|
+
catch { }
|
|
49
60
|
};
|
|
50
61
|
}
|
|
51
62
|
return () => {
|
package/dist/browser/errors.js
CHANGED
|
@@ -6,41 +6,47 @@ export function installBrowserErrorHooks(tracer, sessionId) {
|
|
|
6
6
|
}
|
|
7
7
|
const onError = (ev) => {
|
|
8
8
|
addBreadcrumb({ timestamp: Date.now(), category: "error", message: ev.message || "window.error", level: "error" });
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
9
|
+
try {
|
|
10
|
+
const breadcrumbs = getBreadcrumbs();
|
|
11
|
+
const span = tracer.startSpan("browser.error", {
|
|
12
|
+
attributes: {
|
|
13
|
+
"error.message": ev.message || "window.error",
|
|
14
|
+
"error.file": ev.filename || "",
|
|
15
|
+
"error.line": ev.lineno || 0,
|
|
16
|
+
"error.column": ev.colno || 0,
|
|
17
|
+
"breadcrumbs.count": breadcrumbs.length,
|
|
18
|
+
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-20)),
|
|
19
|
+
...(sessionId ? { "session.id": sessionId } : {}),
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: ev.message });
|
|
23
|
+
if (ev.error instanceof Error) {
|
|
24
|
+
span.recordException(ev.error);
|
|
25
|
+
}
|
|
26
|
+
span.end();
|
|
24
27
|
}
|
|
25
|
-
|
|
28
|
+
catch { }
|
|
26
29
|
};
|
|
27
30
|
const onRejection = (ev) => {
|
|
28
31
|
const reason = typeof ev.reason === "string" ? ev.reason : JSON.stringify(ev.reason ?? {});
|
|
29
32
|
addBreadcrumb({ timestamp: Date.now(), category: "error", message: reason, level: "error" });
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
try {
|
|
34
|
+
const breadcrumbs = getBreadcrumbs();
|
|
35
|
+
const span = tracer.startSpan("browser.unhandledrejection", {
|
|
36
|
+
attributes: {
|
|
37
|
+
"error.message": reason,
|
|
38
|
+
"breadcrumbs.count": breadcrumbs.length,
|
|
39
|
+
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-20)),
|
|
40
|
+
...(sessionId ? { "session.id": sessionId } : {}),
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: reason });
|
|
44
|
+
if (ev.reason instanceof Error) {
|
|
45
|
+
span.recordException(ev.reason);
|
|
46
|
+
}
|
|
47
|
+
span.end();
|
|
42
48
|
}
|
|
43
|
-
|
|
49
|
+
catch { }
|
|
44
50
|
};
|
|
45
51
|
window.addEventListener("error", onError);
|
|
46
52
|
window.addEventListener("unhandledrejection", onRejection);
|
package/dist/browser/index.js
CHANGED
|
@@ -245,9 +245,16 @@ export function initBrowserSDK(config) {
|
|
|
245
245
|
pendingBeaconBlob = null;
|
|
246
246
|
}
|
|
247
247
|
};
|
|
248
|
-
const onVisibility = () => {
|
|
249
|
-
|
|
250
|
-
|
|
248
|
+
const onVisibility = () => {
|
|
249
|
+
if (document.visibilityState === "hidden") {
|
|
250
|
+
otel.forceFlush();
|
|
251
|
+
flushReplay();
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
const onBeforeUnload = () => {
|
|
255
|
+
otel.forceFlush();
|
|
256
|
+
sendViaBeacon();
|
|
257
|
+
};
|
|
251
258
|
if (typeof document !== "undefined") {
|
|
252
259
|
document.addEventListener("visibilitychange", onVisibility);
|
|
253
260
|
cleanups.push(() => document.removeEventListener("visibilitychange", onVisibility));
|
|
@@ -27840,13 +27840,24 @@ function setupOtelWeb(config) {
|
|
|
27840
27840
|
tracerProvider,
|
|
27841
27841
|
instrumentations
|
|
27842
27842
|
});
|
|
27843
|
-
const tracer = trace.getTracer("@obtrace/sdk-browser", "
|
|
27844
|
-
const meter = metrics.getMeter("@obtrace/sdk-browser", "
|
|
27843
|
+
const tracer = trace.getTracer("@obtrace/sdk-browser", "2.1.0");
|
|
27844
|
+
const meter = metrics.getMeter("@obtrace/sdk-browser", "2.1.0");
|
|
27845
|
+
const forceFlush = async () => {
|
|
27846
|
+
try {
|
|
27847
|
+
await tracerProvider.forceFlush();
|
|
27848
|
+
} catch {
|
|
27849
|
+
}
|
|
27850
|
+
try {
|
|
27851
|
+
await meterProvider.forceFlush();
|
|
27852
|
+
} catch {
|
|
27853
|
+
}
|
|
27854
|
+
};
|
|
27845
27855
|
const shutdown = async () => {
|
|
27856
|
+
await forceFlush();
|
|
27846
27857
|
await tracerProvider.shutdown();
|
|
27847
27858
|
await meterProvider.shutdown();
|
|
27848
27859
|
};
|
|
27849
|
-
return { tracer, meter, shutdown };
|
|
27860
|
+
return { tracer, meter, shutdown, forceFlush };
|
|
27850
27861
|
}
|
|
27851
27862
|
|
|
27852
27863
|
// src/browser/breadcrumbs.ts
|
|
@@ -27894,41 +27905,47 @@ function installBrowserErrorHooks(tracer, sessionId) {
|
|
|
27894
27905
|
}
|
|
27895
27906
|
const onError = (ev) => {
|
|
27896
27907
|
addBreadcrumb({ timestamp: Date.now(), category: "error", message: ev.message || "window.error", level: "error" });
|
|
27897
|
-
|
|
27898
|
-
|
|
27899
|
-
|
|
27900
|
-
|
|
27901
|
-
|
|
27902
|
-
|
|
27903
|
-
|
|
27904
|
-
|
|
27905
|
-
|
|
27906
|
-
|
|
27908
|
+
try {
|
|
27909
|
+
const breadcrumbs = getBreadcrumbs();
|
|
27910
|
+
const span = tracer.startSpan("browser.error", {
|
|
27911
|
+
attributes: {
|
|
27912
|
+
"error.message": ev.message || "window.error",
|
|
27913
|
+
"error.file": ev.filename || "",
|
|
27914
|
+
"error.line": ev.lineno || 0,
|
|
27915
|
+
"error.column": ev.colno || 0,
|
|
27916
|
+
"breadcrumbs.count": breadcrumbs.length,
|
|
27917
|
+
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-20)),
|
|
27918
|
+
...sessionId ? { "session.id": sessionId } : {}
|
|
27919
|
+
}
|
|
27920
|
+
});
|
|
27921
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: ev.message });
|
|
27922
|
+
if (ev.error instanceof Error) {
|
|
27923
|
+
span.recordException(ev.error);
|
|
27907
27924
|
}
|
|
27908
|
-
|
|
27909
|
-
|
|
27910
|
-
if (ev.error instanceof Error) {
|
|
27911
|
-
span.recordException(ev.error);
|
|
27925
|
+
span.end();
|
|
27926
|
+
} catch {
|
|
27912
27927
|
}
|
|
27913
|
-
span.end();
|
|
27914
27928
|
};
|
|
27915
27929
|
const onRejection = (ev) => {
|
|
27916
27930
|
const reason = typeof ev.reason === "string" ? ev.reason : JSON.stringify(ev.reason ?? {});
|
|
27917
27931
|
addBreadcrumb({ timestamp: Date.now(), category: "error", message: reason, level: "error" });
|
|
27918
|
-
|
|
27919
|
-
|
|
27920
|
-
|
|
27921
|
-
|
|
27922
|
-
|
|
27923
|
-
|
|
27924
|
-
|
|
27932
|
+
try {
|
|
27933
|
+
const breadcrumbs = getBreadcrumbs();
|
|
27934
|
+
const span = tracer.startSpan("browser.unhandledrejection", {
|
|
27935
|
+
attributes: {
|
|
27936
|
+
"error.message": reason,
|
|
27937
|
+
"breadcrumbs.count": breadcrumbs.length,
|
|
27938
|
+
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-20)),
|
|
27939
|
+
...sessionId ? { "session.id": sessionId } : {}
|
|
27940
|
+
}
|
|
27941
|
+
});
|
|
27942
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: reason });
|
|
27943
|
+
if (ev.reason instanceof Error) {
|
|
27944
|
+
span.recordException(ev.reason);
|
|
27925
27945
|
}
|
|
27926
|
-
|
|
27927
|
-
|
|
27928
|
-
if (ev.reason instanceof Error) {
|
|
27929
|
-
span.recordException(ev.reason);
|
|
27946
|
+
span.end();
|
|
27947
|
+
} catch {
|
|
27930
27948
|
}
|
|
27931
|
-
span.end();
|
|
27932
27949
|
};
|
|
27933
27950
|
window.addEventListener("error", onError);
|
|
27934
27951
|
window.addEventListener("unhandledrejection", onRejection);
|
|
@@ -28218,30 +28235,40 @@ function installConsoleCapture(tracer, sessionId) {
|
|
|
28218
28235
|
const level = LEVEL_MAP[method];
|
|
28219
28236
|
console[method] = (...args) => {
|
|
28220
28237
|
original(...args);
|
|
28221
|
-
|
|
28222
|
-
|
|
28223
|
-
|
|
28224
|
-
const
|
|
28225
|
-
|
|
28226
|
-
|
|
28227
|
-
|
|
28228
|
-
|
|
28229
|
-
|
|
28238
|
+
try {
|
|
28239
|
+
let message;
|
|
28240
|
+
let attrs = {};
|
|
28241
|
+
const safeStringify = (v) => {
|
|
28242
|
+
try {
|
|
28243
|
+
return JSON.stringify(v);
|
|
28244
|
+
} catch {
|
|
28245
|
+
return String(v);
|
|
28246
|
+
}
|
|
28247
|
+
};
|
|
28248
|
+
if (args.length === 1 && typeof args[0] === "object" && args[0] !== null && !Array.isArray(args[0])) {
|
|
28249
|
+
const obj = args[0];
|
|
28250
|
+
message = String(obj.msg || obj.message || safeStringify(obj));
|
|
28251
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
28252
|
+
if (k === "msg" || k === "message") continue;
|
|
28253
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
28254
|
+
attrs[k] = v;
|
|
28255
|
+
}
|
|
28230
28256
|
}
|
|
28257
|
+
} else {
|
|
28258
|
+
message = args.map((a) => typeof a === "string" ? a : safeStringify(a)).join(" ");
|
|
28231
28259
|
}
|
|
28232
|
-
|
|
28233
|
-
|
|
28260
|
+
addBreadcrumb({ timestamp: Date.now(), category: `console.${method}`, message, level, data: attrs });
|
|
28261
|
+
const span = tracer.startSpan("browser.console", {
|
|
28262
|
+
attributes: {
|
|
28263
|
+
"log.severity": level.toUpperCase(),
|
|
28264
|
+
"log.message": message.slice(0, 1024),
|
|
28265
|
+
"session.id": sessionId,
|
|
28266
|
+
...attrs
|
|
28267
|
+
}
|
|
28268
|
+
});
|
|
28269
|
+
span.end();
|
|
28270
|
+
} catch {
|
|
28234
28271
|
}
|
|
28235
|
-
addBreadcrumb({ timestamp: Date.now(), category: `console.${method}`, message, level, data: attrs });
|
|
28236
|
-
const span = tracer.startSpan("browser.console", {
|
|
28237
|
-
attributes: {
|
|
28238
|
-
"log.severity": level.toUpperCase(),
|
|
28239
|
-
"log.message": message.slice(0, 1024),
|
|
28240
|
-
"session.id": sessionId,
|
|
28241
|
-
...attrs
|
|
28242
|
-
}
|
|
28243
|
-
});
|
|
28244
|
-
span.end();
|
|
28245
28272
|
};
|
|
28246
28273
|
}
|
|
28247
28274
|
return () => {
|
|
@@ -28610,9 +28637,15 @@ function initBrowserSDK(config) {
|
|
|
28610
28637
|
}
|
|
28611
28638
|
};
|
|
28612
28639
|
const onVisibility = () => {
|
|
28613
|
-
if (document.visibilityState === "hidden")
|
|
28640
|
+
if (document.visibilityState === "hidden") {
|
|
28641
|
+
otel.forceFlush();
|
|
28642
|
+
flushReplay();
|
|
28643
|
+
}
|
|
28644
|
+
};
|
|
28645
|
+
const onBeforeUnload = () => {
|
|
28646
|
+
otel.forceFlush();
|
|
28647
|
+
sendViaBeacon();
|
|
28614
28648
|
};
|
|
28615
|
-
const onBeforeUnload = () => sendViaBeacon();
|
|
28616
28649
|
if (typeof document !== "undefined") {
|
|
28617
28650
|
document.addEventListener("visibilitychange", onVisibility);
|
|
28618
28651
|
cleanups.push(() => document.removeEventListener("visibilitychange", onVisibility));
|