first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
# `observability/metrics`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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.