# OTel instruments — `@suaveplan/api-auth-bearer-static` _(no submodules)_ ## Purpose The telemetry module owns the single OpenTelemetry counter this package emits — `api.auth.bearer_static_match_total` — partitioned by the documented `outcome` attribute. Every call to a resolver's `resolve()` method increments this counter exactly once with one of four label values. The instrument is bound lazily against `metrics.getMeter(...)` so the package is safe to import on the cold path before the host installs a `MeterProvider`. Resetting the cached bundle (via `resetBearerStaticInstruments`) is required in test setup to bind against a freshly-installed in-memory exporter. ## Features - Single counter — `api.auth.bearer_static_match_total`. - Four `outcome` label values: `match`, `no-token`, `unknown-token`, `mtls-required`. - Lazy meter resolution — module load does not trigger an OTel side effect. - Idempotent reset hook for test isolation. ## Usage: Basic ```ts import { recordMatchOutcome } from "@suaveplan/api-auth-bearer-static/src/telemetry"; // Inside a resolver's resolve() body: recordMatchOutcome("unknown-token"); ``` ## Usage: Advanced ```ts import { getBearerStaticInstruments, resetBearerStaticInstruments, } from "@suaveplan/api-auth-bearer-static/src/telemetry"; // In a vitest setup file that has installed an in-memory MeterProvider // upstream: beforeEach(() => { resetBearerStaticInstruments(); }); it("emits the outcome attribute we expect", async () => { const inst = getBearerStaticInstruments(); inst.matchTotal.add(1, { outcome: "match" }); // ...then flush + assert on the exporter }); ``` ## API Reference - `interface BearerStaticInstruments { matchTotal: Counter }` - `type BearerStaticMatchOutcome = "match" | "no-token" | "unknown-token" | "mtls-required"` - `getBearerStaticInstruments(): BearerStaticInstruments` - `resetBearerStaticInstruments(): void` - `recordMatchOutcome(outcome: BearerStaticMatchOutcome): void` ## Implementation Notes The instrument name uses dotted notation per OpenTelemetry semantic conventions; the attribute key is `outcome` rather than the host-wide `auth.outcome` since this counter is adapter-scoped and dashboards aggregate across adapters by metric name, not attribute. The `description` field is human-readable only — OTel collectors do not interpret it. The cached bundle exists per process and is intentionally global; resolvers do not own instruments because every concrete resolver in a chain would otherwise double-publish the same counter under the same scope.