@obtrace/browser 2.2.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.
@@ -1,4 +1,5 @@
1
1
  import { SpanStatusCode } from "@opentelemetry/api";
2
+ import { SeverityNumber } from "@opentelemetry/api-logs";
2
3
  import { addBreadcrumb } from "./breadcrumbs";
3
4
  const LEVEL_MAP = {
4
5
  debug: "debug",
@@ -7,9 +8,16 @@ const LEVEL_MAP = {
7
8
  warn: "warn",
8
9
  error: "error",
9
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
+ };
10
18
  let patched = false;
11
19
  let originals = {};
12
- export function installConsoleCapture(tracer, sessionId) {
20
+ export function installConsoleCapture(tracer, logger, sessionId) {
13
21
  if (patched || typeof console === "undefined")
14
22
  return () => { };
15
23
  patched = true;
@@ -70,21 +78,30 @@ export function installConsoleCapture(tracer, sessionId) {
70
78
  }
71
79
  }
72
80
  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, {
81
+ logger.emit({
82
+ severityNumber: SEVERITY_MAP[method] ?? SeverityNumber.INFO,
83
+ severityText: level.toUpperCase(),
84
+ body: message.slice(0, 4096),
75
85
  attributes: {
76
- "log.severity": level.toUpperCase(),
77
- "log.message": message.slice(0, 1024),
78
86
  "session.id": sessionId,
87
+ "log.source": "console",
79
88
  ...attrs,
80
89
  },
81
90
  });
82
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
+ });
83
100
  span.setStatus({ code: SpanStatusCode.ERROR, message: message.slice(0, 1024) });
84
101
  if (isErrorObj)
85
102
  span.recordException(firstArg);
103
+ span.end();
86
104
  }
87
- span.end();
88
105
  }
89
106
  catch { }
90
107
  };
@@ -1,6 +1,7 @@
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
  }
@@ -10,19 +11,28 @@ export function installBrowserErrorHooks(tracer, sessionId) {
10
11
  try {
11
12
  const breadcrumbs = getBreadcrumbs();
12
13
  const stack = ev.error instanceof Error ? ev.error.stack || "" : "";
13
- const span = tracer.startSpan("browser.error", {
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,
14
30
  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 } : {}),
31
+ "log.source": "window.error",
32
+ ...attrs,
24
33
  },
25
34
  });
35
+ const span = tracer.startSpan("browser.error", { attributes: attrs });
26
36
  span.setStatus({ code: SpanStatusCode.ERROR, message });
27
37
  if (ev.error instanceof Error) {
28
38
  span.recordException(ev.error);
@@ -34,9 +44,11 @@ export function installBrowserErrorHooks(tracer, sessionId) {
34
44
  const onRejection = (ev) => {
35
45
  let reason;
36
46
  let stack = "";
47
+ let errorType = "UnhandledRejection";
37
48
  if (ev.reason instanceof Error) {
38
49
  reason = `${ev.reason.name}: ${ev.reason.message}`;
39
50
  stack = ev.reason.stack || "";
51
+ errorType = ev.reason.constructor?.name || "Error";
40
52
  }
41
53
  else {
42
54
  reason = typeof ev.reason === "string" ? ev.reason : JSON.stringify(ev.reason ?? {});
@@ -44,16 +56,24 @@ export function installBrowserErrorHooks(tracer, sessionId) {
44
56
  addBreadcrumb({ timestamp: Date.now(), category: "error", message: reason, level: "error" });
45
57
  try {
46
58
  const breadcrumbs = getBreadcrumbs();
47
- const span = tracer.startSpan("browser.unhandledrejection", {
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,
48
71
  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 } : {}),
72
+ "log.source": "unhandledrejection",
73
+ ...attrs,
55
74
  },
56
75
  });
76
+ const span = tracer.startSpan("browser.unhandledrejection", { attributes: attrs });
57
77
  span.setStatus({ code: SpanStatusCode.ERROR, message: reason });
58
78
  if (ev.reason instanceof Error) {
59
79
  span.recordException(ev.reason);
@@ -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";
@@ -165,6 +166,7 @@ export function initBrowserSDK(config) {
165
166
  const otel = setupOtelWeb({ ...config, tracesSampleRate: sampleRate, sessionId: replay.sessionId });
166
167
  const tracer = otel.tracer;
167
168
  const meter = otel.meter;
169
+ const logger = otel.loggerProvider.getLogger("@obtrace/sdk-browser", "2.2.0");
168
170
  const client = new ObtraceClient({
169
171
  ...config,
170
172
  replay: {
@@ -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
- const span = tracer.startSpan("browser.log", {
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
- span.end();
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)),