# `observability/metrics` _(no submodules)_ ## Purpose OpenTelemetry instrument factory + recorders for the OTP family. Emits two duration histograms (`generate` / `verify`), one outcome counter, and one error counter. All instruments are lazily resolved via `metrics.getMeter` so a host that swaps the global `MeterProvider` after import (e.g. test fixtures) still observes emissions against the intended provider. ## Features - Histograms: `otp.generate_duration_ms{type,algorithm}`, `otp.verify_duration_ms{type,algorithm}`. - Counter: `otp.verify_outcome_total{outcome,delta_bucket}` — `outcome` ∈ `valid | invalid`, `delta_bucket` ∈ `0 | +-1 | +-2..N | n/a` (low-cardinality). - Counter: `otp.errors_total{code}` — bucketed by stable error code. - `resetOtpMetricsForTesting()` clears the instrument cache. ## Usage: Basic Example ```ts import { recordVerify } from "@suaveplan/otp/observability"; const start = performance.now(); const r = verify(code, secret); recordVerify("totp", "SHA1", performance.now() - start); ``` ## Usage: Advanced Example ```ts import { recordGenerate, recordOutcome, resetOtpMetricsForTesting, } from "@suaveplan/otp/observability"; beforeAll(() => { metrics.setGlobalMeterProvider(testProvider); resetOtpMetricsForTesting(); }); ``` ## API Reference - `recordGenerate(kind, algorithm, ms)` — records duration histogram. - `recordVerify(kind, algorithm, ms)` — same for verify. - `recordOutcome(outcome, delta)` — increments outcome counter; delta is bucketed via `deltaBucket`. - `recordOtpError(code)` — increments error counter. - `deltaBucket(delta)` — pure mapping helper. - `resetOtpMetricsForTesting()` — clears the instrument cache. ## Implementation Notes The `delta_bucket` enum has cardinality 4 to keep dashboards cheap; emitting raw `delta` values would explode metric storage. The cache is module-level and lazy so the first call after an `import` resolves the `Meter` against whatever `MeterProvider` is current at that moment. The four instruments are intentionally decoupled from the OTP code paths so that suppressing observability (e.g. in a security-restricted environment) is a one-line change at the call site rather than a deep refactor. The recorder helpers do nothing measurable when the global `MeterProvider` is the OpenTelemetry no-op default, which keeps the cost near zero in environments that do not opt in to metrics.