@obtrace/browser 2.0.1 → 2.2.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/browser/console.js +64 -23
- package/dist/browser/errors.js +51 -31
- package/dist/browser/index.js +16 -9
- package/dist/browser_entry.bundle.js +134 -61
- package/dist/browser_entry.bundle.js.map +2 -2
- package/dist/core/otel-web-setup.js +15 -3
- package/dist/types/browser/console.d.ts +1 -1
- package/dist/types/core/otel-web-setup.d.ts +4 -1
- package/dist/types/wrappers/frontend/react.d.ts +2 -0
- package/dist/wrappers/frontend/react.js +57 -0
- 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/browser/console.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SpanStatusCode } from "@opentelemetry/api";
|
|
1
2
|
import { addBreadcrumb } from "./breadcrumbs";
|
|
2
3
|
const LEVEL_MAP = {
|
|
3
4
|
debug: "debug",
|
|
@@ -20,32 +21,72 @@ export function installConsoleCapture(tracer, sessionId) {
|
|
|
20
21
|
const level = LEVEL_MAP[method];
|
|
21
22
|
console[method] = (...args) => {
|
|
22
23
|
original(...args);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
try {
|
|
25
|
+
let message;
|
|
26
|
+
let attrs = {};
|
|
27
|
+
const safeStringify = (v) => {
|
|
28
|
+
try {
|
|
29
|
+
return JSON.stringify(v);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return String(v);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const firstArg = args[0];
|
|
36
|
+
const isErrorObj = firstArg instanceof Error;
|
|
37
|
+
if (isErrorObj) {
|
|
38
|
+
const err = firstArg;
|
|
39
|
+
message = `${err.name}: ${err.message}`;
|
|
40
|
+
if (err.stack)
|
|
41
|
+
attrs["error.stack"] = err.stack.slice(0, 4096);
|
|
42
|
+
attrs["error.type"] = err.name;
|
|
43
|
+
for (let i = 1; i < args.length; i++) {
|
|
44
|
+
const extra = args[i];
|
|
45
|
+
if (typeof extra === "string" && extra.includes("\n at ")) {
|
|
46
|
+
attrs["error.component_stack"] = extra.slice(0, 4096);
|
|
47
|
+
}
|
|
33
48
|
}
|
|
34
49
|
}
|
|
50
|
+
else if (args.length === 1 && typeof firstArg === "object" && firstArg !== null && !Array.isArray(firstArg)) {
|
|
51
|
+
const obj = firstArg;
|
|
52
|
+
message = String(obj.msg || obj.message || safeStringify(obj));
|
|
53
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
54
|
+
if (k === "msg" || k === "message")
|
|
55
|
+
continue;
|
|
56
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
57
|
+
attrs[k] = v;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
message = args.map(a => typeof a === "string" ? a : safeStringify(a)).join(" ");
|
|
63
|
+
if (method === "error") {
|
|
64
|
+
for (const a of args) {
|
|
65
|
+
if (typeof a === "string" && a.includes("\n at ")) {
|
|
66
|
+
attrs["error.stack"] = a.slice(0, 4096);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
addBreadcrumb({ timestamp: Date.now(), category: `console.${method}`, message, level, data: attrs });
|
|
73
|
+
const spanName = (method === "error" && (isErrorObj || attrs["error.stack"])) ? "browser.error" : "browser.console";
|
|
74
|
+
const span = tracer.startSpan(spanName, {
|
|
75
|
+
attributes: {
|
|
76
|
+
"log.severity": level.toUpperCase(),
|
|
77
|
+
"log.message": message.slice(0, 1024),
|
|
78
|
+
"session.id": sessionId,
|
|
79
|
+
...attrs,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
if (method === "error") {
|
|
83
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: message.slice(0, 1024) });
|
|
84
|
+
if (isErrorObj)
|
|
85
|
+
span.recordException(firstArg);
|
|
86
|
+
}
|
|
87
|
+
span.end();
|
|
35
88
|
}
|
|
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();
|
|
89
|
+
catch { }
|
|
49
90
|
};
|
|
50
91
|
}
|
|
51
92
|
return () => {
|
package/dist/browser/errors.js
CHANGED
|
@@ -5,42 +5,62 @@ export function installBrowserErrorHooks(tracer, sessionId) {
|
|
|
5
5
|
return () => undefined;
|
|
6
6
|
}
|
|
7
7
|
const onError = (ev) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
8
|
+
const message = ev.message || "window.error";
|
|
9
|
+
addBreadcrumb({ timestamp: Date.now(), category: "error", message, level: "error" });
|
|
10
|
+
try {
|
|
11
|
+
const breadcrumbs = getBreadcrumbs();
|
|
12
|
+
const stack = ev.error instanceof Error ? ev.error.stack || "" : "";
|
|
13
|
+
const span = tracer.startSpan("browser.error", {
|
|
14
|
+
attributes: {
|
|
15
|
+
"error.message": message,
|
|
16
|
+
"error.file": ev.filename || "",
|
|
17
|
+
"error.line": ev.lineno || 0,
|
|
18
|
+
"error.column": ev.colno || 0,
|
|
19
|
+
"error.stack": stack.slice(0, 4096),
|
|
20
|
+
"error.type": ev.error?.constructor?.name || "Error",
|
|
21
|
+
"breadcrumbs.count": breadcrumbs.length,
|
|
22
|
+
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-20)),
|
|
23
|
+
...(sessionId ? { "session.id": sessionId } : {}),
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message });
|
|
27
|
+
if (ev.error instanceof Error) {
|
|
28
|
+
span.recordException(ev.error);
|
|
29
|
+
}
|
|
30
|
+
span.end();
|
|
24
31
|
}
|
|
25
|
-
|
|
32
|
+
catch { }
|
|
26
33
|
};
|
|
27
34
|
const onRejection = (ev) => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const breadcrumbs = getBreadcrumbs();
|
|
31
|
-
const span = tracer.startSpan("browser.unhandledrejection", {
|
|
32
|
-
attributes: {
|
|
33
|
-
"error.message": reason,
|
|
34
|
-
"breadcrumbs.count": breadcrumbs.length,
|
|
35
|
-
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-20)),
|
|
36
|
-
...(sessionId ? { "session.id": sessionId } : {}),
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
span.setStatus({ code: SpanStatusCode.ERROR, message: reason });
|
|
35
|
+
let reason;
|
|
36
|
+
let stack = "";
|
|
40
37
|
if (ev.reason instanceof Error) {
|
|
41
|
-
|
|
38
|
+
reason = `${ev.reason.name}: ${ev.reason.message}`;
|
|
39
|
+
stack = ev.reason.stack || "";
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
reason = typeof ev.reason === "string" ? ev.reason : JSON.stringify(ev.reason ?? {});
|
|
43
|
+
}
|
|
44
|
+
addBreadcrumb({ timestamp: Date.now(), category: "error", message: reason, level: "error" });
|
|
45
|
+
try {
|
|
46
|
+
const breadcrumbs = getBreadcrumbs();
|
|
47
|
+
const span = tracer.startSpan("browser.unhandledrejection", {
|
|
48
|
+
attributes: {
|
|
49
|
+
"error.message": reason,
|
|
50
|
+
"error.stack": stack.slice(0, 4096),
|
|
51
|
+
"error.type": ev.reason?.constructor?.name || "UnhandledRejection",
|
|
52
|
+
"breadcrumbs.count": breadcrumbs.length,
|
|
53
|
+
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-20)),
|
|
54
|
+
...(sessionId ? { "session.id": sessionId } : {}),
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: reason });
|
|
58
|
+
if (ev.reason instanceof Error) {
|
|
59
|
+
span.recordException(ev.reason);
|
|
60
|
+
}
|
|
61
|
+
span.end();
|
|
42
62
|
}
|
|
43
|
-
|
|
63
|
+
catch { }
|
|
44
64
|
};
|
|
45
65
|
window.addEventListener("error", onError);
|
|
46
66
|
window.addEventListener("unhandledrejection", onRejection);
|
package/dist/browser/index.js
CHANGED
|
@@ -157,7 +157,12 @@ export function initBrowserSDK(config) {
|
|
|
157
157
|
const sampleRate = config.tracesSampleRate ?? 1;
|
|
158
158
|
const replaySampleRate = config.replaySampleRate ?? 1;
|
|
159
159
|
const shouldReplay = Math.random() < replaySampleRate;
|
|
160
|
-
const
|
|
160
|
+
const replay = new BrowserReplayBuffer({
|
|
161
|
+
maxChunkBytes: config.replay?.maxChunkBytes ?? 480_000,
|
|
162
|
+
flushIntervalMs: config.replay?.flushIntervalMs ?? 5000,
|
|
163
|
+
sessionStorageKey: config.replay?.sessionStorageKey ?? "obtrace_session_id",
|
|
164
|
+
});
|
|
165
|
+
const otel = setupOtelWeb({ ...config, tracesSampleRate: sampleRate, sessionId: replay.sessionId });
|
|
161
166
|
const tracer = otel.tracer;
|
|
162
167
|
const meter = otel.meter;
|
|
163
168
|
const client = new ObtraceClient({
|
|
@@ -173,11 +178,6 @@ export function initBrowserSDK(config) {
|
|
|
173
178
|
vitals: { enabled: true, reportAllChanges: false, ...config.vitals },
|
|
174
179
|
propagation: { enabled: true, ...config.propagation },
|
|
175
180
|
});
|
|
176
|
-
const replay = new BrowserReplayBuffer({
|
|
177
|
-
maxChunkBytes: config.replay?.maxChunkBytes ?? 480_000,
|
|
178
|
-
flushIntervalMs: config.replay?.flushIntervalMs ?? 5000,
|
|
179
|
-
sessionStorageKey: config.replay?.sessionStorageKey ?? "obtrace_session_id",
|
|
180
|
-
});
|
|
181
181
|
const recipeSteps = [];
|
|
182
182
|
const cleanups = [];
|
|
183
183
|
const entry = { client, sessionId: replay.sessionId, replay, config, recipeSteps, otel };
|
|
@@ -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));
|
|
@@ -27784,6 +27784,7 @@ function setupOtelWeb(config) {
|
|
|
27784
27784
|
...config.projectId ? { "obtrace.project_id": config.projectId } : {},
|
|
27785
27785
|
...config.appId ? { "obtrace.app_id": config.appId } : {},
|
|
27786
27786
|
...config.env ? { "obtrace.env": config.env } : {},
|
|
27787
|
+
...config.sessionId ? { "session.id": config.sessionId } : {},
|
|
27787
27788
|
"runtime.name": "browser"
|
|
27788
27789
|
});
|
|
27789
27790
|
const traceExporter = new OTLPTraceExporter({
|
|
@@ -27840,13 +27841,24 @@ function setupOtelWeb(config) {
|
|
|
27840
27841
|
tracerProvider,
|
|
27841
27842
|
instrumentations
|
|
27842
27843
|
});
|
|
27843
|
-
const tracer = trace.getTracer("@obtrace/sdk-browser", "
|
|
27844
|
-
const meter = metrics.getMeter("@obtrace/sdk-browser", "
|
|
27844
|
+
const tracer = trace.getTracer("@obtrace/sdk-browser", "2.1.0");
|
|
27845
|
+
const meter = metrics.getMeter("@obtrace/sdk-browser", "2.1.0");
|
|
27846
|
+
const forceFlush = async () => {
|
|
27847
|
+
try {
|
|
27848
|
+
await tracerProvider.forceFlush();
|
|
27849
|
+
} catch {
|
|
27850
|
+
}
|
|
27851
|
+
try {
|
|
27852
|
+
await meterProvider.forceFlush();
|
|
27853
|
+
} catch {
|
|
27854
|
+
}
|
|
27855
|
+
};
|
|
27845
27856
|
const shutdown = async () => {
|
|
27857
|
+
await forceFlush();
|
|
27846
27858
|
await tracerProvider.shutdown();
|
|
27847
27859
|
await meterProvider.shutdown();
|
|
27848
27860
|
};
|
|
27849
|
-
return { tracer, meter, shutdown };
|
|
27861
|
+
return { tracer, meter, shutdown, forceFlush };
|
|
27850
27862
|
}
|
|
27851
27863
|
|
|
27852
27864
|
// src/browser/breadcrumbs.ts
|
|
@@ -27893,42 +27905,61 @@ function installBrowserErrorHooks(tracer, sessionId) {
|
|
|
27893
27905
|
return () => void 0;
|
|
27894
27906
|
}
|
|
27895
27907
|
const onError = (ev) => {
|
|
27896
|
-
|
|
27897
|
-
|
|
27898
|
-
|
|
27899
|
-
|
|
27900
|
-
|
|
27901
|
-
|
|
27902
|
-
|
|
27903
|
-
|
|
27904
|
-
|
|
27905
|
-
|
|
27906
|
-
|
|
27908
|
+
const message = ev.message || "window.error";
|
|
27909
|
+
addBreadcrumb({ timestamp: Date.now(), category: "error", message, level: "error" });
|
|
27910
|
+
try {
|
|
27911
|
+
const breadcrumbs = getBreadcrumbs();
|
|
27912
|
+
const stack = ev.error instanceof Error ? ev.error.stack || "" : "";
|
|
27913
|
+
const span = tracer.startSpan("browser.error", {
|
|
27914
|
+
attributes: {
|
|
27915
|
+
"error.message": message,
|
|
27916
|
+
"error.file": ev.filename || "",
|
|
27917
|
+
"error.line": ev.lineno || 0,
|
|
27918
|
+
"error.column": ev.colno || 0,
|
|
27919
|
+
"error.stack": stack.slice(0, 4096),
|
|
27920
|
+
"error.type": ev.error?.constructor?.name || "Error",
|
|
27921
|
+
"breadcrumbs.count": breadcrumbs.length,
|
|
27922
|
+
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-20)),
|
|
27923
|
+
...sessionId ? { "session.id": sessionId } : {}
|
|
27924
|
+
}
|
|
27925
|
+
});
|
|
27926
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message });
|
|
27927
|
+
if (ev.error instanceof Error) {
|
|
27928
|
+
span.recordException(ev.error);
|
|
27907
27929
|
}
|
|
27908
|
-
|
|
27909
|
-
|
|
27910
|
-
if (ev.error instanceof Error) {
|
|
27911
|
-
span.recordException(ev.error);
|
|
27930
|
+
span.end();
|
|
27931
|
+
} catch {
|
|
27912
27932
|
}
|
|
27913
|
-
span.end();
|
|
27914
27933
|
};
|
|
27915
27934
|
const onRejection = (ev) => {
|
|
27916
|
-
|
|
27935
|
+
let reason;
|
|
27936
|
+
let stack = "";
|
|
27937
|
+
if (ev.reason instanceof Error) {
|
|
27938
|
+
reason = `${ev.reason.name}: ${ev.reason.message}`;
|
|
27939
|
+
stack = ev.reason.stack || "";
|
|
27940
|
+
} else {
|
|
27941
|
+
reason = typeof ev.reason === "string" ? ev.reason : JSON.stringify(ev.reason ?? {});
|
|
27942
|
+
}
|
|
27917
27943
|
addBreadcrumb({ timestamp: Date.now(), category: "error", message: reason, level: "error" });
|
|
27918
|
-
|
|
27919
|
-
|
|
27920
|
-
|
|
27921
|
-
|
|
27922
|
-
|
|
27923
|
-
|
|
27924
|
-
|
|
27944
|
+
try {
|
|
27945
|
+
const breadcrumbs = getBreadcrumbs();
|
|
27946
|
+
const span = tracer.startSpan("browser.unhandledrejection", {
|
|
27947
|
+
attributes: {
|
|
27948
|
+
"error.message": reason,
|
|
27949
|
+
"error.stack": stack.slice(0, 4096),
|
|
27950
|
+
"error.type": ev.reason?.constructor?.name || "UnhandledRejection",
|
|
27951
|
+
"breadcrumbs.count": breadcrumbs.length,
|
|
27952
|
+
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-20)),
|
|
27953
|
+
...sessionId ? { "session.id": sessionId } : {}
|
|
27954
|
+
}
|
|
27955
|
+
});
|
|
27956
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: reason });
|
|
27957
|
+
if (ev.reason instanceof Error) {
|
|
27958
|
+
span.recordException(ev.reason);
|
|
27925
27959
|
}
|
|
27926
|
-
|
|
27927
|
-
|
|
27928
|
-
if (ev.reason instanceof Error) {
|
|
27929
|
-
span.recordException(ev.reason);
|
|
27960
|
+
span.end();
|
|
27961
|
+
} catch {
|
|
27930
27962
|
}
|
|
27931
|
-
span.end();
|
|
27932
27963
|
};
|
|
27933
27964
|
window.addEventListener("error", onError);
|
|
27934
27965
|
window.addEventListener("unhandledrejection", onRejection);
|
|
@@ -28218,30 +28249,66 @@ function installConsoleCapture(tracer, sessionId) {
|
|
|
28218
28249
|
const level = LEVEL_MAP[method];
|
|
28219
28250
|
console[method] = (...args) => {
|
|
28220
28251
|
original(...args);
|
|
28221
|
-
|
|
28222
|
-
|
|
28223
|
-
|
|
28224
|
-
const
|
|
28225
|
-
|
|
28226
|
-
|
|
28227
|
-
|
|
28228
|
-
|
|
28229
|
-
|
|
28252
|
+
try {
|
|
28253
|
+
let message;
|
|
28254
|
+
let attrs = {};
|
|
28255
|
+
const safeStringify = (v) => {
|
|
28256
|
+
try {
|
|
28257
|
+
return JSON.stringify(v);
|
|
28258
|
+
} catch {
|
|
28259
|
+
return String(v);
|
|
28260
|
+
}
|
|
28261
|
+
};
|
|
28262
|
+
const firstArg = args[0];
|
|
28263
|
+
const isErrorObj = firstArg instanceof Error;
|
|
28264
|
+
if (isErrorObj) {
|
|
28265
|
+
const err = firstArg;
|
|
28266
|
+
message = `${err.name}: ${err.message}`;
|
|
28267
|
+
if (err.stack) attrs["error.stack"] = err.stack.slice(0, 4096);
|
|
28268
|
+
attrs["error.type"] = err.name;
|
|
28269
|
+
for (let i = 1; i < args.length; i++) {
|
|
28270
|
+
const extra = args[i];
|
|
28271
|
+
if (typeof extra === "string" && extra.includes("\n at ")) {
|
|
28272
|
+
attrs["error.component_stack"] = extra.slice(0, 4096);
|
|
28273
|
+
}
|
|
28274
|
+
}
|
|
28275
|
+
} else if (args.length === 1 && typeof firstArg === "object" && firstArg !== null && !Array.isArray(firstArg)) {
|
|
28276
|
+
const obj = firstArg;
|
|
28277
|
+
message = String(obj.msg || obj.message || safeStringify(obj));
|
|
28278
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
28279
|
+
if (k === "msg" || k === "message") continue;
|
|
28280
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
28281
|
+
attrs[k] = v;
|
|
28282
|
+
}
|
|
28283
|
+
}
|
|
28284
|
+
} else {
|
|
28285
|
+
message = args.map((a) => typeof a === "string" ? a : safeStringify(a)).join(" ");
|
|
28286
|
+
if (method === "error") {
|
|
28287
|
+
for (const a of args) {
|
|
28288
|
+
if (typeof a === "string" && a.includes("\n at ")) {
|
|
28289
|
+
attrs["error.stack"] = a.slice(0, 4096);
|
|
28290
|
+
break;
|
|
28291
|
+
}
|
|
28292
|
+
}
|
|
28230
28293
|
}
|
|
28231
28294
|
}
|
|
28232
|
-
|
|
28233
|
-
|
|
28234
|
-
|
|
28235
|
-
|
|
28236
|
-
|
|
28237
|
-
|
|
28238
|
-
|
|
28239
|
-
|
|
28240
|
-
|
|
28241
|
-
|
|
28295
|
+
addBreadcrumb({ timestamp: Date.now(), category: `console.${method}`, message, level, data: attrs });
|
|
28296
|
+
const spanName = method === "error" && (isErrorObj || attrs["error.stack"]) ? "browser.error" : "browser.console";
|
|
28297
|
+
const span = tracer.startSpan(spanName, {
|
|
28298
|
+
attributes: {
|
|
28299
|
+
"log.severity": level.toUpperCase(),
|
|
28300
|
+
"log.message": message.slice(0, 1024),
|
|
28301
|
+
"session.id": sessionId,
|
|
28302
|
+
...attrs
|
|
28303
|
+
}
|
|
28304
|
+
});
|
|
28305
|
+
if (method === "error") {
|
|
28306
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: message.slice(0, 1024) });
|
|
28307
|
+
if (isErrorObj) span.recordException(firstArg);
|
|
28242
28308
|
}
|
|
28243
|
-
|
|
28244
|
-
|
|
28309
|
+
span.end();
|
|
28310
|
+
} catch {
|
|
28311
|
+
}
|
|
28245
28312
|
};
|
|
28246
28313
|
}
|
|
28247
28314
|
return () => {
|
|
@@ -28525,7 +28592,12 @@ function initBrowserSDK(config) {
|
|
|
28525
28592
|
const sampleRate = config.tracesSampleRate ?? 1;
|
|
28526
28593
|
const replaySampleRate = config.replaySampleRate ?? 1;
|
|
28527
28594
|
const shouldReplay = Math.random() < replaySampleRate;
|
|
28528
|
-
const
|
|
28595
|
+
const replay = new BrowserReplayBuffer({
|
|
28596
|
+
maxChunkBytes: config.replay?.maxChunkBytes ?? 48e4,
|
|
28597
|
+
flushIntervalMs: config.replay?.flushIntervalMs ?? 5e3,
|
|
28598
|
+
sessionStorageKey: config.replay?.sessionStorageKey ?? "obtrace_session_id"
|
|
28599
|
+
});
|
|
28600
|
+
const otel = setupOtelWeb({ ...config, tracesSampleRate: sampleRate, sessionId: replay.sessionId });
|
|
28529
28601
|
const tracer = otel.tracer;
|
|
28530
28602
|
const meter = otel.meter;
|
|
28531
28603
|
const client = new ObtraceClient({
|
|
@@ -28541,11 +28613,6 @@ function initBrowserSDK(config) {
|
|
|
28541
28613
|
vitals: { enabled: true, reportAllChanges: false, ...config.vitals },
|
|
28542
28614
|
propagation: { enabled: true, ...config.propagation }
|
|
28543
28615
|
});
|
|
28544
|
-
const replay = new BrowserReplayBuffer({
|
|
28545
|
-
maxChunkBytes: config.replay?.maxChunkBytes ?? 48e4,
|
|
28546
|
-
flushIntervalMs: config.replay?.flushIntervalMs ?? 5e3,
|
|
28547
|
-
sessionStorageKey: config.replay?.sessionStorageKey ?? "obtrace_session_id"
|
|
28548
|
-
});
|
|
28549
28616
|
const recipeSteps = [];
|
|
28550
28617
|
const cleanups = [];
|
|
28551
28618
|
const entry = { client, sessionId: replay.sessionId, replay, config, recipeSteps, otel };
|
|
@@ -28610,9 +28677,15 @@ function initBrowserSDK(config) {
|
|
|
28610
28677
|
}
|
|
28611
28678
|
};
|
|
28612
28679
|
const onVisibility = () => {
|
|
28613
|
-
if (document.visibilityState === "hidden")
|
|
28680
|
+
if (document.visibilityState === "hidden") {
|
|
28681
|
+
otel.forceFlush();
|
|
28682
|
+
flushReplay();
|
|
28683
|
+
}
|
|
28684
|
+
};
|
|
28685
|
+
const onBeforeUnload = () => {
|
|
28686
|
+
otel.forceFlush();
|
|
28687
|
+
sendViaBeacon();
|
|
28614
28688
|
};
|
|
28615
|
-
const onBeforeUnload = () => sendViaBeacon();
|
|
28616
28689
|
if (typeof document !== "undefined") {
|
|
28617
28690
|
document.addEventListener("visibilitychange", onVisibility);
|
|
28618
28691
|
cleanups.push(() => document.removeEventListener("visibilitychange", onVisibility));
|