A Vue Plugin That Reconstructs User Sessions Without the RUM Tax
Most teams either skip frontend observability or reach for a commercial RUM product that adds latency, cost, and vendor lock-in. This plugin gives you the one thing that actually solves incidents — a breadcrumb trail with a shared traceId — in under 200 lines, with privacy and volume controls built in from the start.
A single `createBehaviorMonitor` plugin mounts globally in a Vue 3 app and collects the full user journey: route enter/leave, click targets (auto-resolved from `data-track` or innerText), unhandled JS and Promise errors, and Axios responses exceeding a 3-second threshold. Every event carries a session-scoped traceId, a page path, and a timestamp, so backend logs can reconstruct exactly what the user did before a failure. The plugin queues events in memory and flushes them in batches on an 8-second timer, on errors, or on `beforeunload`, using `sendBeacon` with a `fetch` keepalive fallback.
To keep log volume sane, the approach layers four frequency controls: sampling non-critical clicks at 20–30%, debouncing repeated clicks on the same target within 800ms, restricting collection to a whitelist of core business pages, and skipping input values entirely — only field names are recorded. Critical buttons get explicit `data-track` attributes and are always collected. An Axios interceptor wrapper timestamps every request and fires a tracked event when a call takes longer than 3 seconds or fails outright, attaching the HTTP status and error message.
A single traceId then ties together the click that triggered a request, the slow or failed API call, and the route the user was on, turning a vague "button didn't work" report into a concrete timeline the backend can correlate with its own logs.
Most frontend observability debates miss the point: the goal is not exhaustive logging but reconstructing the user's last N steps before a failure. A 30-event ring buffer with a traceId does that; a million-row click table does not.
The plugin's architecture — queue locally, flush on a timer or on error — mirrors what expensive RUM products do under the hood, but without the CDN dependency, the sampling bias, or the per-event pricing.
Privacy compliance is handled by design, not by policy: input values are never captured, only field names. That sidesteps the GDPR/CCPA headaches that sink many homegrown monitoring efforts.
Explicit `data-track` attributes on critical buttons are the difference between knowing 'someone clicked Delete' and knowing 'someone clicked Delete on order #45821.' The latter lets you reproduce the bug; the former is noise.
The Axios interceptor pattern — attaching a start timestamp to `config.metadata` — is a zero-dependency trick that works with any HTTP client, not just Axios, and avoids polluting request payloads.
Sampling non-critical clicks at 30% and debouncing repeated clicks is a pragmatic middle ground: it preserves enough signal to spot UX dead zones while keeping the event stream cheap enough to run on a modest backend.