Files
skills/docs/observability/monitor/analytics-browser/src/sampling/sampling.md
2026-05-29 08:20:56 +01:00

2.5 KiB

sampling.ts

(no submodules)

Purpose

Implements head-based sampling for @suaveplan/analytics-browser. The tracker calls sampler.shouldSample(event) after the consent gate but before the scrubber so events that will be dropped never pay the cost of property scrubbing or queue persistence. HeadSampler accepts a base sample rate in [0, 1] plus an optional Record<eventName, rate> override map so common events (e.g. page) can be captured at a different rate than rare events (e.g. error_reported).

Features

  • Per-event-name overrides — new HeadSampler(0.1, { error_reported: 1 }) keeps every error while sampling everything else at 10 %.
  • Strict clamping — rates <= 0 always drop, rates >= 1 always keep; NaN clamps to 0.
  • Injectable random source — the constructor accepts a random: () => number seam so tests can drive deterministic decisions without monkey-patching Math.random.
  • Stateless — every call is independent; safe to share a sampler across many trackers.

Usage: Basic

import { HeadSampler } from "@suaveplan/analytics-browser/sampling";

const sampler = new HeadSampler(0.25); // keep 25 % of events

Usage: Advanced

import { HeadSampler } from "@suaveplan/analytics-browser/sampling";

// 1 % sampling for verbose page-view events, 100 % for clicks and errors.
const sampler = new HeadSampler(0.01, {
    clicked: 1,
    error_reported: 1,
});

// In tests, inject a deterministic counter so behaviour is reproducible.
let n = 0;
const deterministic = new HeadSampler(0.5, {}, () => {
    const sequence = [0.4, 0.6, 0.2, 0.8];
    return sequence[n++ % sequence.length] as number;
});

API Reference

  • class HeadSampler implements Sampler
    • new HeadSampler(rate, overrides?, random?)
      • rate: number — base rate in [0, 1]. Values outside this range clamp.
      • overrides?: Readonly<Record<string, number>> — per-event-name rate. Each entry clamps independently.
      • random?: () => number — random source; defaults to Math.random.
    • shouldSample(event): boolean — true keeps the event, false drops it.

Implementation Notes

The clamp logic guards against floating-point drift and pathological caller input (e.g. accidentally passing rate=2). NaN clamps to 0 rather than 1 so a misconfigured environment variable does not silently flood the backend. The injected random source enables deterministic testing without resorting to vi.stubGlobal (banned by §2 of AGENTS.md).