@monoscopetech/browser 0.6.1 → 0.7.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Monoscope Browser SDK
2
2
 
3
- The **Monoscope Browser SDK** is a lightweight JavaScript library for adding **session replay**, **performance tracing**, and **frontend logging** to your web applications.
3
+ The **Monoscope Browser SDK** is a lightweight JavaScript library for adding **session replay**, **performance tracing**, **error tracking**, and **web vitals** to your web applications.
4
4
 
5
5
  When used together with the [Monoscope Server SDKs](https://apitoolkit.io/docs/sdks/), you gain **end-to-end observability** — seamlessly connecting user interactions in the browser to backend services, APIs, and databases.
6
6
 
@@ -8,9 +8,9 @@ This means you can:
8
8
 
9
9
  - **Replay user sessions** to see exactly what happened.
10
10
  - **Trace requests** from the frontend, through your backend, and into your database.
11
- - **Capture logs and errors** with full context for faster debugging.
12
-
13
- With the sdk, you can seamlessly monitor how users interact with your app, measure performance, and gain insights into issues — all in one place.
11
+ - **Capture errors and console logs** with full context and breadcrumbs for faster debugging.
12
+ - **Collect Web Vitals** (CLS, INP, LCP, FCP, TTFB) automatically.
13
+ - **Track SPA navigations** across pushState, replaceState, and popstate.
14
14
 
15
15
  ---
16
16
 
@@ -40,7 +40,12 @@ import Monoscope from "@monoscopetech/browser";
40
40
  const monoscope = new Monoscope({
41
41
  projectId: "YOUR_PROJECT_ID",
42
42
  serviceName: "my-web-app",
43
- // ...other configuration options
43
+ });
44
+
45
+ // Identify the current user
46
+ monoscope.setUser({
47
+ id: "user-123",
48
+ email: "user@example.com",
44
49
  });
45
50
  ```
46
51
 
@@ -50,16 +55,22 @@ const monoscope = new Monoscope({
50
55
 
51
56
  The `Monoscope` constructor accepts the following options:
52
57
 
53
- | Name | Type | Description |
54
- | ------------------------------ | --------------------- | ---------------------------------------------------------------------------- |
55
- | `projectId` | `string` | **Required** – Your Monoscope project ID. |
56
- | `serviceName` | `string` | **Required** – Name of your service or application. |
57
- | `exporterEndpoint` | `string` | Endpoint for exporting traces/logs. Defaults to Monoscope's ingest endpoint. |
58
- | `propagateTraceHeaderCorsUrls` | `RegExp[]` | Array of regex patterns for URLs where trace headers should be propagated. |
59
- | `resourceAttributes` | `Record<string, any>` | Additional resource-level attributes. |
60
- | `instrumentations` | `any[]` | OpenTelemetry instrumentations to enable. |
61
- | `replayEventsBaseUrl` | `string` | Base URL for session replay events. Defaults to Monoscope's ingest endpoint. |
62
- | `user` | `MonoscopeUser` | Default user information for the session. |
58
+ | Name | Type | Description |
59
+ | --- | --- | --- |
60
+ | `projectId` | `string` | **Required** – Your Monoscope project ID. |
61
+ | `serviceName` | `string` | **Required** – Name of your service or application. |
62
+ | `exporterEndpoint` | `string` | Endpoint for exporting traces. Defaults to Monoscope's ingest endpoint. |
63
+ | `propagateTraceHeaderCorsUrls` | `RegExp[]` | URL patterns where trace context headers should be propagated. Defaults to same-origin only. |
64
+ | `resourceAttributes` | `Record<string, string>` | Additional OpenTelemetry resource attributes. |
65
+ | `instrumentations` | `unknown[]` | Additional OpenTelemetry instrumentations to register. |
66
+ | `replayEventsBaseUrl` | `string` | Base URL for session replay events. Defaults to Monoscope's ingest endpoint. |
67
+ | `enableNetworkEvents` | `boolean` | Include network timing events in document load spans. |
68
+ | `user` | `MonoscopeUser` | Default user information for the session. |
69
+ | `debug` | `boolean` | Enable debug logging to the console. |
70
+ | `sampleRate` | `number` | Trace sampling rate from `0` to `1`. Default `1` (100%). |
71
+ | `replaySampleRate` | `number` | Replay sampling rate from `0` to `1`. Default `1` (100%). |
72
+ | `enabled` | `boolean` | Whether to start collecting data immediately. Default `true`. |
73
+ | `resourceTimingThresholdMs` | `number` | Minimum resource duration (ms) to report. Default `200`. |
63
74
 
64
75
  ---
65
76
 
@@ -67,13 +78,15 @@ The `Monoscope` constructor accepts the following options:
67
78
 
68
79
  The `MonoscopeUser` object can contain:
69
80
 
70
- | Name | Type | Description |
71
- | ---------- | ---------- | ------------------------- |
72
- | `email` | `string` | User's email address. |
73
- | `fullName` | `string` | User's full name. |
74
- | `name` | `string` | User's preferred name. |
75
- | `id` | `string` | User's unique identifier. |
76
- | `roles` | `string[]` | User's roles. |
81
+ | Name | Type | Description |
82
+ | --- | --- | --- |
83
+ | `email` | `string` | User's email address. |
84
+ | `full_name` | `string` | User's full name. |
85
+ | `name` | `string` | User's preferred name. |
86
+ | `id` | `string` | User's unique identifier. |
87
+ | `roles` | `string[]` | User's roles. |
88
+
89
+ Additional string-keyed attributes are also accepted and forwarded as custom user attributes.
77
90
 
78
91
  ---
79
92
 
@@ -81,7 +94,7 @@ The `MonoscopeUser` object can contain:
81
94
 
82
95
  ### `setUser(user: MonoscopeUser)`
83
96
 
84
- Associates the given user with the current session.
97
+ Associates the given user with the current session. Can be called at any time.
85
98
 
86
99
  ```javascript
87
100
  monoscope.setUser({
@@ -90,17 +103,121 @@ monoscope.setUser({
90
103
  });
91
104
  ```
92
105
 
93
- ---
106
+ ### `startSpan<T>(name: string, fn: (span: Span) => T): T`
107
+
108
+ Creates a custom OpenTelemetry span. The span is automatically ended when the function returns. Supports async functions.
109
+
110
+ ```javascript
111
+ monoscope.startSpan("checkout", (span) => {
112
+ span.setAttribute("cart.items", 3);
113
+ // ... your logic
114
+ });
115
+ ```
116
+
117
+ ### `recordEvent(name: string, attributes?: Record<string, string | number | boolean>)`
118
+
119
+ Records a custom event as a span with the given attributes.
120
+
121
+ ```javascript
122
+ monoscope.recordEvent("button_click", {
123
+ "button.name": "subscribe",
124
+ "button.variant": "primary",
125
+ });
126
+ ```
94
127
 
95
128
  ### `getSessionId(): string`
96
129
 
97
- Retrieves the current session ID — useful for tagging custom spans or events.
130
+ Returns the current session ID.
131
+
132
+ ### `getTabId(): string`
133
+
134
+ Returns the current tab ID (unique per browser tab).
135
+
136
+ ### `enable()` / `disable()`
137
+
138
+ Dynamically enable or disable all data collection.
139
+
140
+ ```javascript
141
+ monoscope.disable(); // pause collection
142
+ monoscope.enable(); // resume collection
143
+ ```
144
+
145
+ ### `isEnabled(): boolean`
146
+
147
+ Returns whether the SDK is currently enabled.
148
+
149
+ ### `destroy(): Promise<void>`
150
+
151
+ Stops all collection, flushes pending data, and shuts down the OpenTelemetry provider. Call this when your application is being torn down.
98
152
 
99
153
  ```javascript
100
- const sessionId = monoscope.getSessionId();
101
- console.log(sessionId);
154
+ await monoscope.destroy();
155
+ ```
156
+
157
+ ---
158
+
159
+ ## React / Next.js
160
+
161
+ For React apps, use the `@monoscopetech/browser/react` subpath export for idiomatic integration with hooks and context.
162
+
163
+ ```tsx
164
+ import { MonoscopeProvider, useMonoscope, useMonoscopeUser, MonoscopeErrorBoundary } from "@monoscopetech/browser/react";
165
+
166
+ // Wrap your app with MonoscopeProvider
167
+ function App() {
168
+ return (
169
+ <MonoscopeProvider config={{ projectId: "YOUR_PROJECT_ID", serviceName: "my-react-app" }}>
170
+ <MonoscopeErrorBoundary fallback={<div>Something went wrong</div>}>
171
+ <MyApp />
172
+ </MonoscopeErrorBoundary>
173
+ </MonoscopeProvider>
174
+ );
175
+ }
176
+
177
+ // Access the instance via hook
178
+ function MyApp() {
179
+ const monoscope = useMonoscope();
180
+
181
+ // Reactively set user when auth state changes
182
+ useMonoscopeUser(currentUser ? { id: currentUser.id, email: currentUser.email } : null);
183
+
184
+ return <div>...</div>;
185
+ }
102
186
  ```
103
187
 
188
+ **Next.js App Router**: The provider includes `"use client"` — import it in a client component or your root layout.
189
+
190
+ ### React API
191
+
192
+ | Export | Description |
193
+ | --- | --- |
194
+ | `MonoscopeProvider` | Context provider. Creates and destroys the SDK instance. Strict Mode safe. |
195
+ | `useMonoscope()` | Returns the `Monoscope` instance (or `null` during SSR). |
196
+ | `useMonoscopeUser(user)` | Calls `setUser` reactively when the user object changes. |
197
+ | `MonoscopeErrorBoundary` | Error boundary that reports caught errors to Monoscope. Accepts `fallback` prop. |
198
+
199
+ ---
200
+
201
+ ## Features
202
+
203
+ ### Session Replay
204
+ Captures DOM changes via [rrweb](https://github.com/rrweb-io/rrweb) to enable full session replay. Sensitive inputs are masked by default.
205
+
206
+ ### Error Tracking
207
+ Automatically captures `window.onerror`, unhandled promise rejections, and `console.error` calls with stack traces and breadcrumbs.
208
+
209
+ ### SPA Navigation Tracking
210
+ Detects client-side navigations (`pushState`, `replaceState`, `popstate`) and emits navigation spans.
211
+
212
+ ### Web Vitals
213
+ Collects Core Web Vitals (CLS, INP, LCP) and additional metrics (FCP, TTFB) via the [web-vitals](https://github.com/GoogleChrome/web-vitals) library.
214
+
215
+ ### Performance Observers
216
+ Reports long tasks and slow resource loads as spans for performance debugging.
217
+
218
+ ### Session Management
219
+ Sessions persist across page reloads via `sessionStorage` and automatically rotate after 30 minutes of inactivity.
220
+
104
221
  ---
105
222
 
106
223
  ## License
@@ -0,0 +1,9 @@
1
+ export type Breadcrumb = {
2
+ type: "click" | "navigation" | "console.error" | "http" | "custom";
3
+ message: string;
4
+ timestamp: number;
5
+ data?: Record<string, string>;
6
+ };
7
+ export declare function addBreadcrumb(crumb: Omit<Breadcrumb, "timestamp">): void;
8
+ export declare function getBreadcrumbs(): Breadcrumb[];
9
+ export declare function clearBreadcrumbs(): void;
@@ -0,0 +1,13 @@
1
+ const MAX_BREADCRUMBS = 20;
2
+ const buffer = [];
3
+ export function addBreadcrumb(crumb) {
4
+ buffer.push({ ...crumb, timestamp: Date.now() });
5
+ if (buffer.length > MAX_BREADCRUMBS)
6
+ buffer.shift();
7
+ }
8
+ export function getBreadcrumbs() {
9
+ return buffer.slice();
10
+ }
11
+ export function clearBreadcrumbs() {
12
+ buffer.length = 0;
13
+ }
@@ -0,0 +1,17 @@
1
+ import { Span } from "@opentelemetry/api";
2
+ type EmitFn = (name: string, attrs: Record<string, string | number>, configure?: (span: Span) => void) => void;
3
+ export declare class ErrorTracker {
4
+ private emit;
5
+ private errorCount;
6
+ private _active;
7
+ private prevOnError;
8
+ private onUnhandledRejection;
9
+ private origConsoleError;
10
+ private _processing;
11
+ constructor(emit: EmitFn);
12
+ private createErrorSpan;
13
+ start(): void;
14
+ stop(): void;
15
+ getErrorCount(): number;
16
+ }
17
+ export {};
package/dist/errors.js ADDED
@@ -0,0 +1,107 @@
1
+ import { SpanStatusCode } from "@opentelemetry/api";
2
+ import { addBreadcrumb, getBreadcrumbs } from "./breadcrumbs";
3
+ function safeStringify(val) {
4
+ try {
5
+ return JSON.stringify(val);
6
+ }
7
+ catch {
8
+ return `[unserializable: ${typeof val}]`;
9
+ }
10
+ }
11
+ export class ErrorTracker {
12
+ constructor(emit) {
13
+ this.errorCount = 0;
14
+ this._active = false;
15
+ this.prevOnError = null;
16
+ this.onUnhandledRejection = null;
17
+ this.origConsoleError = null;
18
+ this._processing = false;
19
+ this.emit = emit;
20
+ }
21
+ createErrorSpan(spanName, errorType, attrs) {
22
+ this.errorCount++;
23
+ const crumbs = getBreadcrumbs();
24
+ this.emit(spanName, { "error.type": errorType, "error.count": this.errorCount, ...attrs }, (s) => {
25
+ s.setStatus({ code: SpanStatusCode.ERROR });
26
+ if (crumbs.length > 0)
27
+ s.setAttribute("breadcrumbs", safeStringify(crumbs));
28
+ });
29
+ }
30
+ start() {
31
+ if (typeof window === "undefined" || this._active)
32
+ return;
33
+ this._active = true;
34
+ this.prevOnError = window.onerror;
35
+ window.onerror = (event, source, lineno, colno, error) => {
36
+ const attrs = {
37
+ "error.message": typeof event === "string" ? event : event.type,
38
+ };
39
+ if (source)
40
+ attrs["error.source"] = source;
41
+ if (lineno !== undefined)
42
+ attrs["error.lineno"] = lineno;
43
+ if (colno !== undefined)
44
+ attrs["error.colno"] = colno;
45
+ if (error?.stack)
46
+ attrs["error.stack"] = error.stack;
47
+ if (error?.name)
48
+ attrs["error.name"] = error.name;
49
+ this.createErrorSpan("exception", "uncaught_exception", attrs);
50
+ if (typeof this.prevOnError === "function") {
51
+ return this.prevOnError.call(window, event, source, lineno, colno, error);
52
+ }
53
+ };
54
+ this.onUnhandledRejection = (event) => {
55
+ const reason = event.reason;
56
+ const attrs = {};
57
+ if (reason instanceof Error) {
58
+ attrs["error.message"] = reason.message;
59
+ attrs["error.name"] = reason.name;
60
+ if (reason.stack)
61
+ attrs["error.stack"] = reason.stack;
62
+ }
63
+ else {
64
+ attrs["error.message"] = String(reason);
65
+ }
66
+ this.createErrorSpan("unhandled_rejection", "unhandled_rejection", attrs);
67
+ };
68
+ this.origConsoleError = console.error;
69
+ console.error = (...args) => {
70
+ this.origConsoleError?.apply(console, args);
71
+ if (this._processing)
72
+ return;
73
+ this._processing = true;
74
+ try {
75
+ const message = args.map((a) => a instanceof Error ? a.message : typeof a === "string" ? a : safeStringify(a)).join(" ");
76
+ const attrs = { "error.message": message };
77
+ const errorArg = args.find((a) => a instanceof Error);
78
+ if (errorArg) {
79
+ attrs["error.name"] = errorArg.name;
80
+ if (errorArg.stack)
81
+ attrs["error.stack"] = errorArg.stack;
82
+ }
83
+ addBreadcrumb({ type: "console.error", message });
84
+ this.createErrorSpan("console.error", "console_error", attrs);
85
+ }
86
+ finally {
87
+ this._processing = false;
88
+ }
89
+ };
90
+ window.addEventListener("unhandledrejection", this.onUnhandledRejection);
91
+ }
92
+ stop() {
93
+ if (typeof window === "undefined" || !this._active)
94
+ return;
95
+ this._active = false;
96
+ window.onerror = this.prevOnError;
97
+ this.prevOnError = null;
98
+ if (this.onUnhandledRejection) {
99
+ window.removeEventListener("unhandledrejection", this.onUnhandledRejection);
100
+ }
101
+ if (this.origConsoleError) {
102
+ console.error = this.origConsoleError;
103
+ this.origConsoleError = null;
104
+ }
105
+ }
106
+ getErrorCount() { return this.errorCount; }
107
+ }
package/dist/index.d.ts CHANGED
@@ -1,13 +1,32 @@
1
1
  import { MonoscopeReplay } from "./replay";
2
2
  import { OpenTelemetryManager } from "./tracing";
3
3
  import { MonoscopeConfig, MonoscopeUser } from "./types";
4
+ import type { Span } from "@opentelemetry/api";
4
5
  declare class Monoscope {
5
6
  replay: MonoscopeReplay;
6
- config: MonoscopeConfig;
7
+ private config;
7
8
  otel: OpenTelemetryManager;
8
9
  sessionId: string;
10
+ tabId: string;
11
+ private lastActivityTime;
12
+ private errors;
13
+ private vitals;
14
+ private router;
15
+ private _enabled;
9
16
  constructor(config: MonoscopeConfig);
17
+ private resolveSessionId;
18
+ private persistActivity;
19
+ private rotateSession;
20
+ private checkAndRefreshSession;
21
+ private setupActivityTracking;
10
22
  getSessionId(): string;
23
+ getTabId(): string;
11
24
  setUser(u: MonoscopeUser): void;
25
+ startSpan<T>(name: string, fn: (span: Span) => T): T;
26
+ recordEvent(name: string, attributes?: Record<string, string | number | boolean>): void;
27
+ disable(): void;
28
+ enable(): void;
29
+ isEnabled(): boolean;
30
+ destroy(): Promise<void>;
12
31
  }
13
32
  export default Monoscope;
package/dist/index.js CHANGED
@@ -1,30 +1,177 @@
1
1
  import { MonoscopeReplay } from "./replay";
2
2
  import { OpenTelemetryManager } from "./tracing";
3
- import { v4 as uuidv4 } from "uuid";
3
+ import { ErrorTracker } from "./errors";
4
+ import { WebVitalsCollector } from "./web-vitals";
5
+ import { SPARouter } from "./router";
6
+ import { addBreadcrumb, clearBreadcrumbs } from "./breadcrumbs";
7
+ const SESSION_TIMEOUT_MS = 30 * 60 * 1000;
8
+ const LAST_ACTIVITY_KEY = "monoscope-last-activity";
9
+ const isBrowser = typeof window !== "undefined";
4
10
  class Monoscope {
5
11
  constructor(config) {
6
- if (!config.projectId) {
12
+ if (!config.projectId)
7
13
  throw new Error("MonoscopeConfig must include projectId");
14
+ if (!config.serviceName)
15
+ throw new Error("MonoscopeConfig must include serviceName");
16
+ this.config = config;
17
+ this._enabled = config.enabled !== false;
18
+ this.tabId = crypto.randomUUID();
19
+ this.sessionId = isBrowser ? this.resolveSessionId() : crypto.randomUUID();
20
+ this.lastActivityTime = Date.now();
21
+ if (isBrowser)
22
+ this.persistActivity();
23
+ this.replay = new MonoscopeReplay(config, this.sessionId, this.tabId);
24
+ this.otel = new OpenTelemetryManager(config, this.sessionId, this.tabId);
25
+ const emit = (...args) => this.otel.emitSpan(...args);
26
+ this.errors = new ErrorTracker(emit);
27
+ this.vitals = new WebVitalsCollector(emit);
28
+ this.router = new SPARouter(emit);
29
+ if (this._enabled) {
30
+ this.otel.configure();
31
+ this.replay.configure();
32
+ this.errors.start();
33
+ this.vitals.start().catch((e) => {
34
+ if (this.config.debug)
35
+ console.warn("Monoscope: web-vitals init failed", e);
36
+ });
37
+ this.router.start();
38
+ }
39
+ if (isBrowser)
40
+ this.setupActivityTracking();
41
+ }
42
+ resolveSessionId() {
43
+ try {
44
+ const storedId = sessionStorage.getItem("monoscope-session-id");
45
+ const lastActivity = sessionStorage.getItem(LAST_ACTIVITY_KEY);
46
+ if (storedId && lastActivity) {
47
+ const elapsed = Date.now() - parseInt(lastActivity, 10);
48
+ if (elapsed < SESSION_TIMEOUT_MS)
49
+ return storedId;
50
+ }
51
+ const newId = crypto.randomUUID();
52
+ sessionStorage.setItem("monoscope-session-id", newId);
53
+ return newId;
54
+ }
55
+ catch (e) {
56
+ if (this.config.debug)
57
+ console.warn("Monoscope: sessionStorage unavailable, using ephemeral session", e);
58
+ return crypto.randomUUID();
59
+ }
60
+ }
61
+ persistActivity() {
62
+ try {
63
+ sessionStorage.setItem(LAST_ACTIVITY_KEY, Date.now().toString());
8
64
  }
9
- const storedSessionId = sessionStorage.getItem("monoscope-session-id");
10
- if (storedSessionId) {
11
- this.sessionId = storedSessionId;
65
+ catch (e) {
66
+ if (this.config.debug)
67
+ console.warn("Monoscope: failed to persist activity", e);
12
68
  }
13
- else {
14
- this.sessionId = uuidv4();
69
+ }
70
+ rotateSession() {
71
+ this.replay.save().catch((e) => {
72
+ if (this.config.debug)
73
+ console.warn("Monoscope: failed to save replay on session rotation", e);
74
+ });
75
+ clearBreadcrumbs();
76
+ this.sessionId = crypto.randomUUID();
77
+ try {
15
78
  sessionStorage.setItem("monoscope-session-id", this.sessionId);
16
79
  }
17
- this.config = config;
18
- this.replay = new MonoscopeReplay(config, this.sessionId);
19
- this.otel = new OpenTelemetryManager(config, this.sessionId);
20
- this.otel.configure();
21
- this.replay.configure();
80
+ catch (e) {
81
+ if (this.config.debug)
82
+ console.warn("Monoscope: failed to persist session ID", e);
83
+ }
84
+ this.replay.updateSessionId(this.sessionId);
85
+ this.otel.updateSessionId(this.sessionId);
86
+ if (this.config.debug)
87
+ console.log("Monoscope: session rotated due to inactivity");
22
88
  }
23
- getSessionId() {
24
- return this.sessionId;
89
+ checkAndRefreshSession() {
90
+ const now = Date.now();
91
+ if (now - this.lastActivityTime >= SESSION_TIMEOUT_MS) {
92
+ this.rotateSession();
93
+ }
94
+ this.lastActivityTime = now;
95
+ this.persistActivity();
25
96
  }
97
+ setupActivityTracking() {
98
+ document.addEventListener("visibilitychange", () => {
99
+ if (document.visibilityState === "visible") {
100
+ this.checkAndRefreshSession();
101
+ }
102
+ });
103
+ let lastTracked = Date.now();
104
+ const trackActivity = () => {
105
+ const now = Date.now();
106
+ if (now - lastTracked > 5000) {
107
+ lastTracked = now;
108
+ this.lastActivityTime = now;
109
+ this.persistActivity();
110
+ }
111
+ };
112
+ document.addEventListener("click", (e) => {
113
+ trackActivity();
114
+ const el = e.target;
115
+ if (!el)
116
+ return;
117
+ const tag = el.tagName?.toLowerCase() || "";
118
+ const text = (el.textContent || "").trim().slice(0, 50);
119
+ const cls = el.getAttribute("class") || "";
120
+ const selector = el.id ? `#${el.id}` : cls ? `.${cls.split(" ")[0]}` : tag;
121
+ addBreadcrumb({ type: "click", message: `${tag} "${text}"`, data: { selector } });
122
+ }, { capture: true, passive: true });
123
+ document.addEventListener("keydown", trackActivity, { capture: true, passive: true });
124
+ }
125
+ getSessionId() { return this.sessionId; }
126
+ getTabId() { return this.tabId; }
26
127
  setUser(u) {
128
+ if (this.config.debug) {
129
+ const known = new Set(["email", "full_name", "name", "id", "roles"]);
130
+ const extra = Object.keys(u).filter(k => !known.has(k));
131
+ if (extra.length)
132
+ console.warn(`Monoscope: unknown user attributes will be sent to collectors: ${extra.join(", ")}`);
133
+ }
27
134
  this.otel.setUser(u);
135
+ this.replay.setUser(u);
136
+ }
137
+ startSpan(name, fn) {
138
+ return this.otel.startSpan(name, fn);
139
+ }
140
+ recordEvent(name, attributes) {
141
+ this.otel.recordEvent(name, attributes);
142
+ }
143
+ disable() {
144
+ this._enabled = false;
145
+ this.replay.setEnabled(false);
146
+ this.otel.setEnabled(false);
147
+ this.vitals.setEnabled(false);
148
+ this.errors.stop();
149
+ this.router.stop();
150
+ }
151
+ enable() {
152
+ this._enabled = true;
153
+ this.otel.setEnabled(true);
154
+ this.otel.configure();
155
+ this.replay.setEnabled(true);
156
+ this.replay.configure();
157
+ this.vitals.setEnabled(true);
158
+ this.vitals.start().catch(() => { });
159
+ this.errors.start();
160
+ this.router.start();
161
+ }
162
+ isEnabled() { return this._enabled; }
163
+ async destroy() {
164
+ this.errors.stop();
165
+ this.router.stop();
166
+ this.replay.stop();
167
+ this.vitals.setEnabled(false);
168
+ try {
169
+ await this.otel.shutdown();
170
+ }
171
+ catch (e) {
172
+ console.warn("Monoscope: provider shutdown failed, some trace data may be lost", e);
173
+ }
174
+ this._enabled = false;
28
175
  }
29
176
  }
30
177
  export default Monoscope;