# `otel/instruments` _(no submodules)_ ## Purpose OpenTelemetry instrument bundle for the vault-backed secrets adapter. Owns the three counters the package emits and a lazily- bound cache keyed on the global `MeterProvider`. The canonical `api.secrets.reads_total{backend="vault"}` counter is the metric operators correlate across every secrets adapter in the host scope — its label namespace lets a single dashboard ingest reads from env, Bun-native, vault, or future adapters without per-backend counter names. ## Features - `getSecretsVaultInstruments()` — returns the cached bundle, binding to the global `MeterProvider` on first call. - `resetSecretsVaultInstruments()` — drops the cache so the next `get` rebinds against a freshly-installed `MeterProvider`. Used in test setup so each spec starts with a clean read. - `readsTotal` — counter for successful resolutions; the adapter always tags `backend="vault"`. - `writesTotal` — counter for `set` / `delete` mutations on the `fromVault` bridge, partitioned by `op ∈ "set" | "delete"`. (The bridge is the only place writes flow; the adapter is read-only.) - `watcherEmitsTotal` — counter for `secret.changed` events forwarded by the watcher. ## Usage: Basic ```ts import { getSecretsVaultInstruments } from "@suaveplan/api-secrets-vault/otel"; const ins = getSecretsVaultInstruments(); ins.readsTotal.add(1, { backend: "vault" }); ``` ## Usage: Advanced Reset + rebind in a test that installs its own `MeterProvider`: ```ts import { metrics } from "@opentelemetry/api"; import { MeterProvider } from "@opentelemetry/sdk-metrics"; import { getSecretsVaultInstruments, resetSecretsVaultInstruments, } from "@suaveplan/api-secrets-vault/otel"; const provider = new MeterProvider({ readers: [/* exporter */] }); metrics.setGlobalMeterProvider(provider); resetSecretsVaultInstruments(); const ins = getSecretsVaultInstruments(); ins.readsTotal.add(1, { backend: "vault" }); ``` ## API Reference - `interface SecretsVaultInstruments` - `readonly readsTotal: Counter` - `readonly writesTotal: Counter` - `readonly watcherEmitsTotal: Counter` - `getSecretsVaultInstruments(): SecretsVaultInstruments` - `resetSecretsVaultInstruments(): void` ## Implementation Notes The cache holds the bundle across calls so production paths pay the `metrics.getMeter` cost once per process. Each counter description is human-readable; `api.secrets.reads_total` is intentionally a generic name with a `backend` attribute rather than `api.secrets.vault.reads_total` so cross-adapter aggregations are single-counter operations on the metrics backend. Calling `resetSecretsVaultInstruments` between tests is mandatory when a new `MeterProvider` is installed — otherwise the cached counters point at a stale provider and the new exporter sees zero emissions even though `add(1)` was called. The vitest setup at `tests/setup/polyfills.ts` installs its provider before any source import, so the first `get` already binds against it; tests then call `reset` only when they need to verify counter emission against a fresh exporter state.