# otel/instruments _(no submodules)_ ## Purpose Owns the OpenTelemetry instrument bundle the audit plugin emits to every request lifecycle. Resolving the instruments lazily on first call lets test harnesses install a custom `MeterProvider` BEFORE the production module loads, so the in-memory exporter captures every observation. Without this lazy resolution, the meter would bind to the no-op default provider at import time and tests would see no metrics. The four instruments cover the entire surface needed to operate the plugin in production: per-request recorded counter (labelled by outcome and sampled flag), excluded-path counter, sink-failure counter, and a duration histogram. ## Features - Lazy, cached resolution — first call constructs the meter + four instruments; subsequent calls return the same bundle. - `resetAuditPluginInstruments()` test hook drops the cache so a new `MeterProvider` can be picked up between tests. - Counter and histogram surfaces follow the OTel semantic-conventions naming style (`audit._` for histograms, `_total` for counters). ## Usage: Basic ```ts import { getAuditPluginInstruments } from "@suaveplan/api-plugin-audit/otel/instruments"; const instruments = getAuditPluginInstruments(); instruments.requestsRecorded.add(1, { outcome: "success", sampled: "true" }); instruments.middlewareDuration.record(12.4); ``` ## Usage: Advanced ```ts import { metrics } from "@opentelemetry/api"; import { MeterProvider } from "@opentelemetry/sdk-metrics"; import { getAuditPluginInstruments, resetAuditPluginInstruments, } from "@suaveplan/api-plugin-audit/otel/instruments"; const provider = new MeterProvider(); metrics.setGlobalMeterProvider(provider); resetAuditPluginInstruments(); const instruments = getAuditPluginInstruments(); instruments.sinkFailures.add(1); await provider.forceFlush(); ``` ## API Reference - `interface AuditPluginInstruments { meter, requestsRecorded, requestsExcluded, sinkFailures, middlewareDuration }`. - `getAuditPluginInstruments(): AuditPluginInstruments` — cached resolver. - `resetAuditPluginInstruments(): void` — drops the cache. ## Implementation Notes The lazy cache is keyed on a single module-level `let` binding; concurrent first-callers see the same instance because the resolver is synchronous. Histogram unit is explicitly declared `ms` so OTel collectors don't have to infer it from the metric name. The labels on `requestsRecorded` are kept low-cardinality (`outcome ∈ {success, error}` × `sampled ∈ {true, false}`) so the cardinality budget for downstream backends stays bounded.