# otel/instruments _(no submodules)_ ## Purpose The instruments module owns the three OpenTelemetry counters the user plugin emits — `user.profile_reads_total`, `user.profile_updates_total{outcome}`, and `user.self_deletes_total`. The counters are lazily bound to the global `MeterProvider` on first call and cached for the lifetime of the process, with a `resetUserInstruments()` helper so test setup can install a fresh provider and force the next call to rebind. ## Features - Three counters with stable names — they appear verbatim in every exporter (Prometheus, OTLP, in-memory test reader). - Lazy binding — the meter is resolved on first call, so test setup that installs a `MeterProvider` BEFORE importing the user plugin is guaranteed to see its counters bound against that provider. - `outcome` attribute on `profile_updates_total` so a Grafana panel can split success vs. error without an additional counter. - `resetUserInstruments()` clears the cache for tests that swap the global `MeterProvider` mid-suite. ## Usage: Basic ```ts import { getUserInstruments } from "@suaveplan/api-plugin-user/otel/instruments"; const instruments = getUserInstruments(); instruments.profileReadsTotal.add(1); ``` ## Usage: Advanced Tests install a real in-memory MeterProvider in `polyfills.ts` BEFORE any plugin source imports run, then `resetUserInstruments()` after `metrics.setGlobalMeterProvider(...)`: ```ts import { metrics } from "@opentelemetry/api"; import { InMemoryMetricExporter, MeterProvider } from "@opentelemetry/sdk-metrics"; import { resetUserInstruments } from "@suaveplan/api-plugin-user/otel/instruments"; const provider = new MeterProvider({ readers: [/* … */] }); metrics.setGlobalMeterProvider(provider); resetUserInstruments(); ``` ## API Reference ### `getUserInstruments(): UserInstruments` Returns the cached bundle, building it on first call. ### `resetUserInstruments(): void` Clears the module cache. Idempotent. ### `UserInstruments` `{ profileReadsTotal, profileUpdatesTotal, selfDeletesTotal }` — each value is an OpenTelemetry `Counter`. ## Implementation Notes - The meter name is the package name — every exporter that records the meter scope will surface it as `@suaveplan/api-plugin-user`. - The counters are intentionally narrow — histograms / observables belong in a follow-up if and when observability needs them. - The cache invalidation pattern is mirrored from the sibling auth plugin so test setup is uniform across `api-plugin-*` packages.