Files
skills/docs/networking/network/service-discovery/src/otel/otel.md
2026-05-29 08:20:56 +01:00

2.3 KiB

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

import {
  recordRegistration,
  recordQuery,
  recordQueryDuration,
} from "@suaveplan/service-discovery";

recordRegistration("registered");
recordQuery();
recordQueryDuration(1.42);

Usage: Advanced Example

import { bindActiveServicesGauge } from "@suaveplan/service-discovery";

const services = new Set<string>();
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.