# `instruments` _(no submodules)_ ## Purpose Lazily resolves the OpenTelemetry instruments (`Counter`, `Histogram`) the plugin emits from its production paths. The bundle is built on first call and cached; tests call `resetTenantPluginInstruments` after installing a fresh `MeterProvider` so subsequent calls rebind to the test exporter. This pattern lets the package work seamlessly in production (where the global provider is the OTel SDK) and under test (where the global provider is an `InMemoryMetricExporter`). ## Features - Four instruments: `resolutionsTotal` (counter), `resolutionDurationMs` (histogram), `cacheTotal` (counter), `managementOpsTotal` (counter). - Lazy initialisation — the meter is resolved on first call so test polyfills installed before the package loads bind correctly. - `resetTenantPluginInstruments` clears the cache so a re-bind is forced; primarily useful in test setup hooks. ## Usage: Basic Example ```ts import { getTenantPluginInstruments } from "@suaveplan/api-plugin-tenant"; const instruments = getTenantPluginInstruments(); instruments.resolutionsTotal.add(1, { outcome: "resolved", strategy: "header" }); instruments.resolutionDurationMs.record(12.4, { strategy: "header" }); ``` ## Usage: Advanced Example ```ts import { getTenantPluginInstruments, resetTenantPluginInstruments, } from "@suaveplan/api-plugin-tenant"; import { metrics } from "@opentelemetry/api"; import { InMemoryMetricExporter, MeterProvider, PeriodicExportingMetricReader, AggregationTemporality } from "@opentelemetry/sdk-metrics"; // In test setup before the package's other modules load: const exporter = new InMemoryMetricExporter(AggregationTemporality.CUMULATIVE); const reader = new PeriodicExportingMetricReader({ exporter, exportIntervalMillis: 60_000 }); const provider = new MeterProvider({ readers: [reader] }); metrics.setGlobalMeterProvider(provider); resetTenantPluginInstruments(); // Later — assert the counters that were emitted: await provider.forceFlush(); const collected = exporter.getMetrics(); const resolutions = collected[0]?.scopeMetrics[0]?.metrics.find( (m) => m.descriptor.name === "tenant_plugin.resolutions_total" ); console.assert(resolutions !== undefined); ``` ## API Reference - `interface TenantPluginInstruments` — `{ resolutionsTotal: Counter, resolutionDurationMs: Histogram, cacheTotal: Counter, managementOpsTotal: Counter }`. - `function getTenantPluginInstruments(): TenantPluginInstruments` — returns the cached bundle, building it on first call. - `function resetTenantPluginInstruments(): void` — drops the cache. ## Implementation Notes The lazy pattern is essential — calling `metrics.getMeter("...")` at module top-level binds to whichever `MeterProvider` was installed at import time. Because Vitest's `setupFiles` run before `src/` modules import, installing the in-memory provider there guarantees the test bundle binds correctly. Production callers that install their OTel SDK late (e.g. behind a feature flag) call `resetTenantPluginInstruments` once the SDK is up so the plugin rebinds. The instrument names use `tenant_plugin.` rather than `tenant.` so dashboards can cleanly distinguish the plugin's emissions from the underlying `@suaveplan/api-tenant` library's instruments (if any are added later).