# OTel instrumentation _(no submodules)_ ## Purpose Single-source OTel instruments for `@suaveplan/service-discovery`. Centralising counter/histogram/gauge creation here keeps cardinality bounded, avoids the common bug where multiple meters get created under similar names, and gives dashboards a single canonical set of metric IDs to chart. ## Features - `svc_disc.registrations_total{outcome}` counter — outcomes are `registered`, `refreshed`, `rejected` (low-cardinality enum). - `svc_disc.queries_total` counter — incremented once per `query()` call. - `svc_disc.query_duration_ms` histogram — wall-clock latency per query. - `svc_disc.active_services` observable gauge — bound to a registry's `size` getter at construction; the meter pulls on scrape. ## Usage: Basic Example ```ts import { recordRegistration, recordQuery, recordQueryDuration, } from "@suaveplan/service-discovery"; recordRegistration("registered"); recordQuery(); recordQueryDuration(1.42); ``` ## Usage: Advanced Example ```ts import { bindActiveServicesGauge } from "@suaveplan/service-discovery"; const services = new Set(); const dispose = bindActiveServicesGauge(() => services.size); // ...later, on shutdown: dispose(); ``` ## API Reference - `recordRegistration(outcome)` — increments `svc_disc.registrations_total{outcome}` by 1. `outcome` is the `RegistrationOutcome` union. - `recordQuery()` — increments `svc_disc.queries_total` by 1. - `recordQueryDuration(ms)` — records into `svc_disc.query_duration_ms`. Values below zero are clamped to 0. - `bindActiveServicesGauge(getSize)` — registers an OTel observable-gauge callback. Returns a disposer that unbinds the callback. ## Implementation Notes The `outcome` label is restricted to three string values so dashboards can chart success vs refresh vs rejection without unbounded cardinality. The gauge is observable (not synchronous) because the canonical "current size" of the registry is best read on scrape — that way no separate metric write is required on every add/delete and the gauge cannot drift from the actual state. `bindActiveServicesGauge` returns its own disposer so registries can properly tear down their instrumentation on `dispose()` without leaking callbacks across test runs.