@obtrace/browser 2.1.0 → 2.3.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/browser/console.js +54 -7
- package/dist/browser/errors.js +51 -17
- package/dist/browser/index.js +49 -14
- package/dist/browser_entry.bundle.js +2305 -416
- package/dist/browser_entry.bundle.js.map +4 -4
- package/dist/core/otel-web-setup.js +19 -3
- package/dist/types/browser/console.d.ts +3 -2
- package/dist/types/browser/errors.d.ts +2 -1
- package/dist/types/core/otel-web-setup.d.ts +5 -1
- package/dist/types/wrappers/frontend/react.d.ts +2 -0
- package/dist/wrappers/frontend/react.js +57 -0
- package/package.json +3 -1
package/dist/browser/console.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { SpanStatusCode } from "@opentelemetry/api";
|
|
2
|
+
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
1
3
|
import { addBreadcrumb } from "./breadcrumbs";
|
|
2
4
|
const LEVEL_MAP = {
|
|
3
5
|
debug: "debug",
|
|
@@ -6,9 +8,16 @@ const LEVEL_MAP = {
|
|
|
6
8
|
warn: "warn",
|
|
7
9
|
error: "error",
|
|
8
10
|
};
|
|
11
|
+
const SEVERITY_MAP = {
|
|
12
|
+
debug: SeverityNumber.DEBUG,
|
|
13
|
+
log: SeverityNumber.INFO,
|
|
14
|
+
info: SeverityNumber.INFO,
|
|
15
|
+
warn: SeverityNumber.WARN,
|
|
16
|
+
error: SeverityNumber.ERROR,
|
|
17
|
+
};
|
|
9
18
|
let patched = false;
|
|
10
19
|
let originals = {};
|
|
11
|
-
export function installConsoleCapture(tracer, sessionId) {
|
|
20
|
+
export function installConsoleCapture(tracer, logger, sessionId) {
|
|
12
21
|
if (patched || typeof console === "undefined")
|
|
13
22
|
return () => { };
|
|
14
23
|
patched = true;
|
|
@@ -31,8 +40,23 @@ export function installConsoleCapture(tracer, sessionId) {
|
|
|
31
40
|
return String(v);
|
|
32
41
|
}
|
|
33
42
|
};
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
const firstArg = args[0];
|
|
44
|
+
const isErrorObj = firstArg instanceof Error;
|
|
45
|
+
if (isErrorObj) {
|
|
46
|
+
const err = firstArg;
|
|
47
|
+
message = `${err.name}: ${err.message}`;
|
|
48
|
+
if (err.stack)
|
|
49
|
+
attrs["error.stack"] = err.stack.slice(0, 4096);
|
|
50
|
+
attrs["error.type"] = err.name;
|
|
51
|
+
for (let i = 1; i < args.length; i++) {
|
|
52
|
+
const extra = args[i];
|
|
53
|
+
if (typeof extra === "string" && extra.includes("\n at ")) {
|
|
54
|
+
attrs["error.component_stack"] = extra.slice(0, 4096);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else if (args.length === 1 && typeof firstArg === "object" && firstArg !== null && !Array.isArray(firstArg)) {
|
|
59
|
+
const obj = firstArg;
|
|
36
60
|
message = String(obj.msg || obj.message || safeStringify(obj));
|
|
37
61
|
for (const [k, v] of Object.entries(obj)) {
|
|
38
62
|
if (k === "msg" || k === "message")
|
|
@@ -44,17 +68,40 @@ export function installConsoleCapture(tracer, sessionId) {
|
|
|
44
68
|
}
|
|
45
69
|
else {
|
|
46
70
|
message = args.map(a => typeof a === "string" ? a : safeStringify(a)).join(" ");
|
|
71
|
+
if (method === "error") {
|
|
72
|
+
for (const a of args) {
|
|
73
|
+
if (typeof a === "string" && a.includes("\n at ")) {
|
|
74
|
+
attrs["error.stack"] = a.slice(0, 4096);
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
47
79
|
}
|
|
48
80
|
addBreadcrumb({ timestamp: Date.now(), category: `console.${method}`, message, level, data: attrs });
|
|
49
|
-
|
|
81
|
+
logger.emit({
|
|
82
|
+
severityNumber: SEVERITY_MAP[method] ?? SeverityNumber.INFO,
|
|
83
|
+
severityText: level.toUpperCase(),
|
|
84
|
+
body: message.slice(0, 4096),
|
|
50
85
|
attributes: {
|
|
51
|
-
"log.severity": level.toUpperCase(),
|
|
52
|
-
"log.message": message.slice(0, 1024),
|
|
53
86
|
"session.id": sessionId,
|
|
87
|
+
"log.source": "console",
|
|
54
88
|
...attrs,
|
|
55
89
|
},
|
|
56
90
|
});
|
|
57
|
-
|
|
91
|
+
if (method === "error") {
|
|
92
|
+
const spanName = (isErrorObj || attrs["error.stack"]) ? "browser.error" : "browser.console";
|
|
93
|
+
const span = tracer.startSpan(spanName, {
|
|
94
|
+
attributes: {
|
|
95
|
+
"error.message": message.slice(0, 1024),
|
|
96
|
+
"session.id": sessionId,
|
|
97
|
+
...attrs,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: message.slice(0, 1024) });
|
|
101
|
+
if (isErrorObj)
|
|
102
|
+
span.recordException(firstArg);
|
|
103
|
+
span.end();
|
|
104
|
+
}
|
|
58
105
|
}
|
|
59
106
|
catch { }
|
|
60
107
|
};
|
package/dist/browser/errors.js
CHANGED
|
@@ -1,25 +1,39 @@
|
|
|
1
1
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
2
|
+
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
2
3
|
import { addBreadcrumb, getBreadcrumbs } from "./breadcrumbs";
|
|
3
|
-
export function installBrowserErrorHooks(tracer, sessionId) {
|
|
4
|
+
export function installBrowserErrorHooks(tracer, logger, sessionId) {
|
|
4
5
|
if (typeof window === "undefined") {
|
|
5
6
|
return () => undefined;
|
|
6
7
|
}
|
|
7
8
|
const onError = (ev) => {
|
|
8
|
-
|
|
9
|
+
const message = ev.message || "window.error";
|
|
10
|
+
addBreadcrumb({ timestamp: Date.now(), category: "error", message, level: "error" });
|
|
9
11
|
try {
|
|
10
12
|
const breadcrumbs = getBreadcrumbs();
|
|
11
|
-
const
|
|
13
|
+
const stack = ev.error instanceof Error ? ev.error.stack || "" : "";
|
|
14
|
+
const errorType = ev.error?.constructor?.name || "Error";
|
|
15
|
+
const attrs = {
|
|
16
|
+
"error.message": message,
|
|
17
|
+
"error.file": ev.filename || "",
|
|
18
|
+
"error.line": ev.lineno || 0,
|
|
19
|
+
"error.column": ev.colno || 0,
|
|
20
|
+
"error.stack": stack.slice(0, 4096),
|
|
21
|
+
"error.type": errorType,
|
|
22
|
+
"breadcrumbs.count": breadcrumbs.length,
|
|
23
|
+
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-20)),
|
|
24
|
+
...(sessionId ? { "session.id": sessionId } : {}),
|
|
25
|
+
};
|
|
26
|
+
logger.emit({
|
|
27
|
+
severityNumber: SeverityNumber.ERROR,
|
|
28
|
+
severityText: "ERROR",
|
|
29
|
+
body: message,
|
|
12
30
|
attributes: {
|
|
13
|
-
"
|
|
14
|
-
|
|
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 } : {}),
|
|
31
|
+
"log.source": "window.error",
|
|
32
|
+
...attrs,
|
|
20
33
|
},
|
|
21
34
|
});
|
|
22
|
-
span
|
|
35
|
+
const span = tracer.startSpan("browser.error", { attributes: attrs });
|
|
36
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message });
|
|
23
37
|
if (ev.error instanceof Error) {
|
|
24
38
|
span.recordException(ev.error);
|
|
25
39
|
}
|
|
@@ -28,18 +42,38 @@ export function installBrowserErrorHooks(tracer, sessionId) {
|
|
|
28
42
|
catch { }
|
|
29
43
|
};
|
|
30
44
|
const onRejection = (ev) => {
|
|
31
|
-
|
|
45
|
+
let reason;
|
|
46
|
+
let stack = "";
|
|
47
|
+
let errorType = "UnhandledRejection";
|
|
48
|
+
if (ev.reason instanceof Error) {
|
|
49
|
+
reason = `${ev.reason.name}: ${ev.reason.message}`;
|
|
50
|
+
stack = ev.reason.stack || "";
|
|
51
|
+
errorType = ev.reason.constructor?.name || "Error";
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
reason = typeof ev.reason === "string" ? ev.reason : JSON.stringify(ev.reason ?? {});
|
|
55
|
+
}
|
|
32
56
|
addBreadcrumb({ timestamp: Date.now(), category: "error", message: reason, level: "error" });
|
|
33
57
|
try {
|
|
34
58
|
const breadcrumbs = getBreadcrumbs();
|
|
35
|
-
const
|
|
59
|
+
const attrs = {
|
|
60
|
+
"error.message": reason,
|
|
61
|
+
"error.stack": stack.slice(0, 4096),
|
|
62
|
+
"error.type": errorType,
|
|
63
|
+
"breadcrumbs.count": breadcrumbs.length,
|
|
64
|
+
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-20)),
|
|
65
|
+
...(sessionId ? { "session.id": sessionId } : {}),
|
|
66
|
+
};
|
|
67
|
+
logger.emit({
|
|
68
|
+
severityNumber: SeverityNumber.ERROR,
|
|
69
|
+
severityText: "ERROR",
|
|
70
|
+
body: reason,
|
|
36
71
|
attributes: {
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-20)),
|
|
40
|
-
...(sessionId ? { "session.id": sessionId } : {}),
|
|
72
|
+
"log.source": "unhandledrejection",
|
|
73
|
+
...attrs,
|
|
41
74
|
},
|
|
42
75
|
});
|
|
76
|
+
const span = tracer.startSpan("browser.unhandledrejection", { attributes: attrs });
|
|
43
77
|
span.setStatus({ code: SpanStatusCode.ERROR, message: reason });
|
|
44
78
|
if (ev.reason instanceof Error) {
|
|
45
79
|
span.recordException(ev.reason);
|
package/dist/browser/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { record } from "rrweb";
|
|
2
2
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
3
|
+
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
3
4
|
import { ObtraceClient } from "../core/client";
|
|
4
5
|
import { setupOtelWeb } from "../core/otel-web-setup";
|
|
5
6
|
import { installBrowserErrorHooks } from "./errors";
|
|
@@ -157,9 +158,15 @@ export function initBrowserSDK(config) {
|
|
|
157
158
|
const sampleRate = config.tracesSampleRate ?? 1;
|
|
158
159
|
const replaySampleRate = config.replaySampleRate ?? 1;
|
|
159
160
|
const shouldReplay = Math.random() < replaySampleRate;
|
|
160
|
-
const
|
|
161
|
+
const replay = new BrowserReplayBuffer({
|
|
162
|
+
maxChunkBytes: config.replay?.maxChunkBytes ?? 480_000,
|
|
163
|
+
flushIntervalMs: config.replay?.flushIntervalMs ?? 5000,
|
|
164
|
+
sessionStorageKey: config.replay?.sessionStorageKey ?? "obtrace_session_id",
|
|
165
|
+
});
|
|
166
|
+
const otel = setupOtelWeb({ ...config, tracesSampleRate: sampleRate, sessionId: replay.sessionId });
|
|
161
167
|
const tracer = otel.tracer;
|
|
162
168
|
const meter = otel.meter;
|
|
169
|
+
const logger = otel.loggerProvider.getLogger("@obtrace/sdk-browser", "2.2.0");
|
|
163
170
|
const client = new ObtraceClient({
|
|
164
171
|
...config,
|
|
165
172
|
replay: {
|
|
@@ -173,11 +180,6 @@ export function initBrowserSDK(config) {
|
|
|
173
180
|
vitals: { enabled: true, reportAllChanges: false, ...config.vitals },
|
|
174
181
|
propagation: { enabled: true, ...config.propagation },
|
|
175
182
|
});
|
|
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
183
|
const recipeSteps = [];
|
|
182
184
|
const cleanups = [];
|
|
183
185
|
const entry = { client, sessionId: replay.sessionId, replay, config, recipeSteps, otel };
|
|
@@ -207,14 +209,14 @@ export function initBrowserSDK(config) {
|
|
|
207
209
|
}).catch(() => { });
|
|
208
210
|
if (config.vitals?.enabled !== false)
|
|
209
211
|
cleanups.push(installWebVitals(meter, !!config.vitals?.reportAllChanges));
|
|
210
|
-
cleanups.push(installBrowserErrorHooks(tracer, replay.sessionId));
|
|
212
|
+
cleanups.push(installBrowserErrorHooks(tracer, logger, replay.sessionId));
|
|
211
213
|
cleanups.push(installClickBreadcrumbs());
|
|
212
214
|
cleanups.push(installClickTracking(tracer, replay.sessionId));
|
|
213
215
|
cleanups.push(installResourceTiming(meter));
|
|
214
216
|
cleanups.push(installLongTaskDetection(tracer));
|
|
215
217
|
cleanups.push(installMemoryTracking(meter));
|
|
216
218
|
if (config.captureConsole !== false) {
|
|
217
|
-
cleanups.push(installConsoleCapture(tracer, replay.sessionId));
|
|
219
|
+
cleanups.push(installConsoleCapture(tracer, logger, replay.sessionId));
|
|
218
220
|
}
|
|
219
221
|
cleanups.push(installOfflineSupport());
|
|
220
222
|
if (shouldReplay && config.replay?.enabled !== false && typeof window !== "undefined") {
|
|
@@ -263,22 +265,40 @@ export function initBrowserSDK(config) {
|
|
|
263
265
|
window.addEventListener("beforeunload", onBeforeUnload);
|
|
264
266
|
cleanups.push(() => window.removeEventListener("beforeunload", onBeforeUnload));
|
|
265
267
|
}
|
|
268
|
+
const sevToOtel = {
|
|
269
|
+
debug: SeverityNumber.DEBUG,
|
|
270
|
+
info: SeverityNumber.INFO,
|
|
271
|
+
warn: SeverityNumber.WARN,
|
|
272
|
+
error: SeverityNumber.ERROR,
|
|
273
|
+
fatal: SeverityNumber.FATAL,
|
|
274
|
+
};
|
|
266
275
|
const log = (level, message, context) => {
|
|
267
276
|
addCrumb({ timestamp: Date.now(), category: "log", message, level: level === "fatal" ? "error" : level });
|
|
268
|
-
|
|
277
|
+
logger.emit({
|
|
278
|
+
severityNumber: sevToOtel[level] ?? SeverityNumber.INFO,
|
|
279
|
+
severityText: level.toUpperCase(),
|
|
280
|
+
body: message.slice(0, 4096),
|
|
269
281
|
attributes: {
|
|
270
|
-
"log.severity": level.toUpperCase(),
|
|
271
|
-
"log.severity_number": severityToNumber(level),
|
|
272
|
-
"log.message": message,
|
|
273
282
|
"session.id": replay.sessionId,
|
|
283
|
+
"log.source": "sdk",
|
|
274
284
|
...userAttrs(),
|
|
275
285
|
...(context?.traceId ? { "obtrace.trace_id": context.traceId } : {}),
|
|
276
286
|
...context?.attrs,
|
|
277
287
|
},
|
|
278
288
|
});
|
|
279
|
-
if (level === "error" || level === "fatal")
|
|
289
|
+
if (level === "error" || level === "fatal") {
|
|
290
|
+
const span = tracer.startSpan("browser.error", {
|
|
291
|
+
attributes: {
|
|
292
|
+
"error.message": message,
|
|
293
|
+
"session.id": replay.sessionId,
|
|
294
|
+
...userAttrs(),
|
|
295
|
+
...(context?.traceId ? { "obtrace.trace_id": context.traceId } : {}),
|
|
296
|
+
...context?.attrs,
|
|
297
|
+
},
|
|
298
|
+
});
|
|
280
299
|
span.setStatus({ code: SpanStatusCode.ERROR, message });
|
|
281
|
-
|
|
300
|
+
span.end();
|
|
301
|
+
}
|
|
282
302
|
};
|
|
283
303
|
const metricFn = (name, value, unit, context) => {
|
|
284
304
|
const gauge = meter.createGauge(name, { unit: unit ?? "1" });
|
|
@@ -286,11 +306,26 @@ export function initBrowserSDK(config) {
|
|
|
286
306
|
};
|
|
287
307
|
const captureException = (error, context) => {
|
|
288
308
|
const msg = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
309
|
+
const stack = error instanceof Error ? (error.stack || "").slice(0, 4096) : "";
|
|
289
310
|
const breadcrumbs = getBreadcrumbs();
|
|
290
311
|
addCrumb({ timestamp: Date.now(), category: "error", message: msg, level: "error" });
|
|
312
|
+
logger.emit({
|
|
313
|
+
severityNumber: SeverityNumber.ERROR,
|
|
314
|
+
severityText: "ERROR",
|
|
315
|
+
body: msg,
|
|
316
|
+
attributes: {
|
|
317
|
+
"session.id": replay.sessionId,
|
|
318
|
+
"log.source": "exception",
|
|
319
|
+
"error.stack": stack,
|
|
320
|
+
"error.type": error instanceof Error ? error.name : "Error",
|
|
321
|
+
...userAttrs(),
|
|
322
|
+
...context?.attrs,
|
|
323
|
+
},
|
|
324
|
+
});
|
|
291
325
|
const span = tracer.startSpan("browser.exception", {
|
|
292
326
|
attributes: {
|
|
293
327
|
"error.message": msg,
|
|
328
|
+
"error.stack": stack,
|
|
294
329
|
"session.id": replay.sessionId,
|
|
295
330
|
"breadcrumbs.count": breadcrumbs.length,
|
|
296
331
|
"breadcrumbs.json": JSON.stringify(breadcrumbs.slice(-20)),
|