# `instruments` — OpenTelemetry instruments for `api-plugin-example` _(no submodules)_ ## Purpose The reference plugin declares three OTel instruments that mirror the `telemetry.metrics[]` block in `plugin.json`. The instruments are lazily materialised against the global `MeterProvider` so the test setup can install an `InMemoryMetricExporter` before the first call. The integration suite reads the in-memory exporter to assert each manifest-declared instrument emits values during the run. ## Features - `example.invoices_finalised_total` — counter, no unit. - `example.stripe.call_duration_ms` — histogram, `unit: "ms"`. - `example.outstanding_invoice_count` — counter modelling a gauge. - Lazy single-shot resolution — second call returns the cached object. - `resetExampleInstrumentsForTest` — clears the cache between suites. ## Usage: Basic ```ts import { getExampleInstruments } from "@suaveplan/api-plugin-example/instruments/instruments"; const m = getExampleInstruments(); m.invoicesFinalisedTotal.add(1, { tenant_id: ctx.tenantId ?? "none" }); ``` ## Usage: Advanced The histogram is recorded around outbound Stripe calls. Wrap the network call to capture duration even on error: ```ts const start = performance.now(); try { await stripe.charges.create({ amount, currency }); } finally { m.stripeCallDurationMs.record(performance.now() - start); } ``` ## API Reference - `getExampleInstruments(): ExampleInstruments` — Lazy accessor. - `resetExampleInstrumentsForTest(): void` — Cache reset. - `ExampleInstruments` — `{ invoicesFinalisedTotal, stripeCallDurationMs, outstandingInvoiceCount }`. ## Implementation Notes The instruments are materialised against `metrics.getMeter(METER_NAME)` rather than a passed-in provider so plugin code does not need to plumb a meter through every handler. The provider is installed once in `tests/setup/polyfills.ts` and the cached registry binds to it on first call. The cache is intentionally process-wide rather than per-`MeterProvider` because plugin authors typically install a global provider at boot and never swap it; tests that DO swap providers call `resetExampleInstrumentsForTest` between cases so the second call re-materialises against the new meter. The OTel SDK's `InMemoryMetricExporter` is the only test substrate that needs the cache reset — production paths bind to the global provider for the lifetime of the host process and any datapoint emitted goes through the same instrument instance. The histogram declares `unit: "ms"` so downstream Prometheus / OTLP collectors expose the unit in their metric metadata. The counter and the gauge-modelling counter both omit the `unit` field because they are dimensionless.