# `instruments` _(no submodules)_ ## Purpose Builds the OTel instrument bundle the header tenant resolver emits on every `resolve()` invocation. Centralising instrument creation keeps the resolver factory free of meter plumbing — the resolver simply records against the frozen counter returned here. The counter name (`api.tenant.resolves_total`) follows the API plugin system design doc §6 step 4 so a missing entry is a spec deviation a CI gate could detect. The instrument is wired against the OTel global meter provider by default, which falls back to a no-op implementation when no SDK is installed. Tests need not configure a provider; production hosts register their SDK once at boot and the resolver picks it up automatically. ## Features - One counter: `api.tenant.resolves_total` labelled `{ resolver: "header", outcome }`. - `outcome` attribute drawn from a closed five-value union (`resolved`, `absent`, `invalid`, `not_found_in_registry`, `untrusted_source`) so dashboards can break down resolver behaviour. - Optional `meterProvider` injection for tests that want a captive provider; defaults to the OTel global. ## Usage: Basic ```ts import { createHeaderResolverInstruments } from "@suaveplan/api-tenant-resolver-header/instruments"; const instruments = createHeaderResolverInstruments(); instruments.resolvesTotal.add(1, { resolver: "header", outcome: "resolved" }); ``` ## Usage: Advanced ```ts import { MeterProvider } from "@opentelemetry/sdk-metrics"; import { createHeaderResolverInstruments } from "@suaveplan/api-tenant-resolver-header/instruments"; const provider = new MeterProvider(); const instruments = createHeaderResolverInstruments({ meterProvider: provider, }); // hook instruments.meter into a Prometheus exporter to scrape // `api_tenant_resolves_total{resolver="header",outcome="invalid"}` etc. ``` ## API Reference - `interface HeaderResolverInstruments` — `{ meter, resolvesTotal }`. - `type HeaderResolveOutcome` — five-value union of outcome attribute values. - `function createHeaderResolverInstruments(config?: { meterProvider? })` — returns a frozen instrument bundle. ## Implementation Notes The bundle is frozen with `Object.freeze` so consumers cannot accidentally swap the counter instance at runtime. The counter is created lazily-per- resolver-instance rather than module-level so a host that wants different meter providers per host instance (multi-tenant test harness) can wire each resolver separately. A counter — not a histogram — is the right primitive here because the quantity being observed is the count of resolver invocations broken down by outcome, not a duration. Resolver latency is dominated by the registry probe; hosts that need probe latency wire a histogram around their `TenantRegistryProbe` rather than expecting this package to time it.