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,103 @@
# otel/instruments
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The instruments module owns the OpenTelemetry counters and histogram
emitted by every code path inside `@suaveplan/api-plugin-auth`. The
bundle is lazily constructed against the global `MeterProvider` so
operators can wire an SDK before the plugin loads without import-time
side effects, and tests can call `resetAuthInstruments()` between
suites to rebind against a fresh in-memory provider.
## Features
- Five named instruments — `sessionsResolvedTotal`,
`sessionResolveDurationMs`, `roleCheckTotal`, `signupsTotal`,
`signinsTotal` — bundled in one `AuthInstruments` value.
- Lazy global-provider binding via `metrics.getMeter(...)` so the
instruments resolve at first call, never at import time.
- `resetAuthInstruments()` for deterministic test setup.
- Stable instrument names (`auth.*`) mirrored by the spec delta so
operators can scrape known metric IDs without code inspection.
## Usage: Basic
```ts
import { getAuthInstruments } from "@suaveplan/api-plugin-auth/otel/instruments";
const ins = getAuthInstruments();
ins.sessionsResolvedTotal.add(1, { outcome: "authenticated" });
ins.sessionResolveDurationMs.record(durationMs, { outcome: "authenticated" });
```
## Usage: Advanced
For testing, install an in-memory `MeterProvider`, then reset the
cached instruments so the next access binds against it. The plugin's
own test polyfill (`tests/setup/polyfills.ts`) does exactly this for
every test file in the package.
```ts
import { metrics } from "@opentelemetry/api";
import {
InMemoryMetricExporter,
MeterProvider,
PeriodicExportingMetricReader,
AggregationTemporality,
} from "@opentelemetry/sdk-metrics";
import {
getAuthInstruments,
resetAuthInstruments,
} from "@suaveplan/api-plugin-auth/otel/instruments";
const exporter = new InMemoryMetricExporter(AggregationTemporality.CUMULATIVE);
const reader = new PeriodicExportingMetricReader({ exporter, exportIntervalMillis: 60_000 });
const provider = new MeterProvider({ readers: [reader] });
metrics.setGlobalMeterProvider(provider);
resetAuthInstruments();
const ins = getAuthInstruments();
ins.signinsTotal.add(1, { outcome: "failure" });
await provider.forceFlush();
const collected = exporter.getMetrics();
```
## API Reference
### `interface AuthInstruments`
- `sessionsResolvedTotal: Counter` — `auth.sessions_resolved_total`.
- `sessionResolveDurationMs: Histogram` — `auth.session_resolve_duration_ms`
(unit `ms`).
- `roleCheckTotal: Counter` — `auth.role_check_total`.
- `signupsTotal: Counter` — `auth.signups_total`.
- `signinsTotal: Counter` — `auth.signins_total`.
### `getAuthInstruments(): AuthInstruments`
Returns the cached bundle, building it on first call. Safe to call
concurrently — the cache is single-writer because the function is
synchronous.
### `resetAuthInstruments(): void`
Drops the cache so the next `getAuthInstruments()` builds afresh
against whatever `MeterProvider` is currently registered.
## Implementation Notes
- The module deliberately reads `metrics.getMeter` lazily — calling it
at import time would resolve the no-op meter before tests install
their provider, which is exactly the failure mode that the
`resetAuthInstruments()` helper exists to prevent.
- Counter / histogram attribute keys (`outcome`) are documented per
call site rather than enforced at the meter — OpenTelemetry's
attribute API is intentionally untyped, but the call-site values are
string literals (`"authenticated"`, `"anonymous"`, `"error"`,
`"allowed"`, `"denied"`, `"success"`, `"failure"`) so dashboards can
rely on the cardinality being bounded.
- Histogram unit is `ms` so dashboards do not need to renormalise.