@heystack/otel 0.11.1 → 0.12.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 +4 -0
- package/dist/web.d.ts +28 -0
- package/dist/web.js +25 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -400,6 +400,8 @@ await instrumentWeb({
|
|
|
400
400
|
|
|
401
401
|
Otherwise it's **cost-aware and safe by design**: off unless you opt in; head-sampled (an unsampled request still propagates `traceparent` with the sampled flag cleared, so the backend makes the same keep/drop decision — no orphaned server spans); and the exporter posts through the *original* `fetch`, never tracing its own upload (no self-export loop). Spans post to `/v1/traces` cross-origin with no CORS setup on your side.
|
|
402
402
|
|
|
403
|
+
**Remote kill-switch (safety valve).** Browser tracing can also be disabled **from the server**, without an app redeploy — a way to stop it fleet-wide if it ever misbehaves in the wild. It is **fail-open**: if the server config is unreachable or omits the flag, tracing stays on (absent flag = enabled), so the switch can only ever *disable*, never accidentally turn a working app off.
|
|
404
|
+
|
|
403
405
|
### Browser error & console collection
|
|
404
406
|
|
|
405
407
|
`instrumentWeb` captures **uncaught browser errors** out of the box — no extra setup. Any `window.onerror` / unhandled promise rejection becomes an OTLP **log** (`event.name=browser.error`, `ERROR` severity) carrying the OTel `exception.type` / `exception.message` / `exception.stacktrace` semconv attributes, the page `url.full`, the `session_id` (so it correlates to the session replay), and — when `tracing` is on — the active `trace_id` / `span_id`. `console.error` is captured too by default.
|
|
@@ -417,6 +419,8 @@ await instrumentWeb({
|
|
|
417
419
|
|
|
418
420
|
It's the **same cost-safe design** as tracing/replay: logs POST to `/v1/logs` through the *original* `fetch` (captured before any patching) so the exporter never traces its own upload — no self-export loop. Console capture is **recursion-guarded** (anything logged on the export path is never re-captured) and **rate-capped** (max ~60 records/minute; the overflow is dropped and counted, with one summary log emitted when the cap lifts) so a runaway `console.error` in a render loop can't flood ingest. Errors show up in the console **Logs** tab and correlate to their session replay and trace.
|
|
419
421
|
|
|
422
|
+
Like tracing, error / console capture has a **remote kill-switch**: it can be disabled **from the server** without an app redeploy, and is **fail-open** (if the server config is unreachable or omits the flag, capture stays on — absent flag = enabled).
|
|
423
|
+
|
|
420
424
|
### In-app bug reports (`reportBug`)
|
|
421
425
|
|
|
422
426
|
Let users report a bug from inside your app. `reportBug` is a headless API — **no widget, you own the UX** — that files a structured report and auto-attaches the context a triager needs: the current URL, user agent, replay `session_id`, active `trace_id`, `release` / `build`, and the **last few captured browser errors** (so you see what was going wrong on the page right before the report). The report also appears on the Logs timeline (`event.name=user.bug_report`).
|
package/dist/web.d.ts
CHANGED
|
@@ -17,6 +17,19 @@ export interface RecorderConfig {
|
|
|
17
17
|
enabled: boolean;
|
|
18
18
|
sample_rate: number;
|
|
19
19
|
masking_mode: "strict" | "relaxed";
|
|
20
|
+
/**
|
|
21
|
+
* Server-side kill-switch for browser distributed tracing. Absent/`true` = enabled
|
|
22
|
+
* (**fail-open**); set `false` server-side to remotely disable browser tracing
|
|
23
|
+
* WITHOUT an app redeploy — the safety valve for the 2026-07-03 cross-origin
|
|
24
|
+
* `traceparent` outage. See `tracingActive`.
|
|
25
|
+
*/
|
|
26
|
+
tracing_enabled?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Server-side kill-switch for browser error / console capture. Absent/`true` =
|
|
29
|
+
* enabled (**fail-open**); set `false` server-side to remotely disable it without
|
|
30
|
+
* an app redeploy. See `errorCaptureActive`.
|
|
31
|
+
*/
|
|
32
|
+
errors_enabled?: boolean;
|
|
20
33
|
}
|
|
21
34
|
/** Pure: decide once per session whether to record. rng defaults to Math.random. */
|
|
22
35
|
export declare function shouldRecord(cfg: RecorderConfig, rng?: () => number): boolean;
|
|
@@ -116,6 +129,21 @@ export interface InstrumentWebOptions {
|
|
|
116
129
|
*/
|
|
117
130
|
captureConsole?: "error" | "warn" | false;
|
|
118
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Pure: is browser distributed tracing active for this run? Requires BOTH the app
|
|
134
|
+
* to have opted in (`opts.tracing`) AND the server-side kill-switch to not be off
|
|
135
|
+
* (`cfg.tracing_enabled`). **Fail-open**: an absent/`true` flag counts as enabled, so
|
|
136
|
+
* an old or unreachable `/v1/replay/config` never silently disables tracing. Split out
|
|
137
|
+
* as a tiny exported predicate so the gating decision is unit-testable without booting
|
|
138
|
+
* rrweb (which `instrumentWeb` imports). See `RecorderConfig.tracing_enabled`.
|
|
139
|
+
*/
|
|
140
|
+
export declare function tracingActive(opts: Pick<InstrumentWebOptions, "tracing">, cfg: Pick<RecorderConfig, "tracing_enabled">): boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Pure: is browser error / console capture active for this run? On by default
|
|
143
|
+
* (`opts.errors !== false`) AND gated by the server-side kill-switch
|
|
144
|
+
* (`cfg.errors_enabled`). **Fail-open**: an absent/`true` flag counts as enabled.
|
|
145
|
+
*/
|
|
146
|
+
export declare function errorCaptureActive(opts: Pick<InstrumentWebOptions, "errors">, cfg: Pick<RecorderConfig, "errors_enabled">): boolean;
|
|
119
147
|
/** Entry point: fetch config, decide sampling, start rrweb, stream chunks.
|
|
120
148
|
* Returns a stop() function. Safe to call in any browser; no-ops on the server. */
|
|
121
149
|
export declare function instrumentWeb(opts: InstrumentWebOptions): Promise<() => void>;
|
package/dist/web.js
CHANGED
|
@@ -81,6 +81,25 @@ export class ReplayTransport {
|
|
|
81
81
|
}
|
|
82
82
|
const DEFAULT_FLUSH_MS = 5000;
|
|
83
83
|
const DEFAULT_FLUSH_EVENTS = 200;
|
|
84
|
+
/**
|
|
85
|
+
* Pure: is browser distributed tracing active for this run? Requires BOTH the app
|
|
86
|
+
* to have opted in (`opts.tracing`) AND the server-side kill-switch to not be off
|
|
87
|
+
* (`cfg.tracing_enabled`). **Fail-open**: an absent/`true` flag counts as enabled, so
|
|
88
|
+
* an old or unreachable `/v1/replay/config` never silently disables tracing. Split out
|
|
89
|
+
* as a tiny exported predicate so the gating decision is unit-testable without booting
|
|
90
|
+
* rrweb (which `instrumentWeb` imports). See `RecorderConfig.tracing_enabled`.
|
|
91
|
+
*/
|
|
92
|
+
export function tracingActive(opts, cfg) {
|
|
93
|
+
return !!opts.tracing && cfg.tracing_enabled !== false;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Pure: is browser error / console capture active for this run? On by default
|
|
97
|
+
* (`opts.errors !== false`) AND gated by the server-side kill-switch
|
|
98
|
+
* (`cfg.errors_enabled`). **Fail-open**: an absent/`true` flag counts as enabled.
|
|
99
|
+
*/
|
|
100
|
+
export function errorCaptureActive(opts, cfg) {
|
|
101
|
+
return opts.errors !== false && cfg.errors_enabled !== false;
|
|
102
|
+
}
|
|
84
103
|
/** Entry point: fetch config, decide sampling, start rrweb, stream chunks.
|
|
85
104
|
* Returns a stop() function. Safe to call in any browser; no-ops on the server. */
|
|
86
105
|
export async function instrumentWeb(opts) {
|
|
@@ -113,7 +132,9 @@ export async function instrumentWeb(opts) {
|
|
|
113
132
|
// a real CLIENT span per outbound fetch + propagates W3C context to the backend.
|
|
114
133
|
let stopTracing = () => { };
|
|
115
134
|
let fetchPatched = false;
|
|
116
|
-
|
|
135
|
+
// Gated by the server kill-switch (cfg.tracing_enabled) as well as the app opt-in —
|
|
136
|
+
// fail-open, so an unreachable/old config can't disable tracing. See tracingActive.
|
|
137
|
+
if (tracingActive(opts, cfg)) {
|
|
117
138
|
const exporter = new BrowserTraceExporter({
|
|
118
139
|
endpoint, apiKey: opts.apiKey, service: opts.service, fetchImpl: originalFetch,
|
|
119
140
|
resourceAttributes: releaseAttrs,
|
|
@@ -157,7 +178,9 @@ export async function instrumentWeb(opts) {
|
|
|
157
178
|
// 2b. Browser error / console log collection — ON by default, INDEPENDENT of both
|
|
158
179
|
// tracing and replay sampling. Exports OTLP logs to /v1/logs via the original fetch.
|
|
159
180
|
let stopErrors = () => { };
|
|
160
|
-
|
|
181
|
+
// Gated by the server kill-switch (cfg.errors_enabled) as well as the opts.errors
|
|
182
|
+
// default — fail-open. See errorCaptureActive.
|
|
183
|
+
if (errorCaptureActive(opts, cfg)) {
|
|
161
184
|
const unpatchErrors = startErrorCapture({
|
|
162
185
|
exporter: logsExporter,
|
|
163
186
|
sessionId,
|