2.4 KiB
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
import { recordVerify } from "@suaveplan/otp/observability";
const start = performance.now();
const r = verify(code, secret);
recordVerify("totp", "SHA1", performance.now() - start);
Usage: Advanced Example
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 viadeltaBucket.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.