Files
suaveplan/packages/api-stack/api/api-tenant-resolver-header/src/instruments/instruments.md
2026-05-27 23:29:53 +01:00

2.8 KiB

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

import { createHeaderResolverInstruments } from "@suaveplan/api-tenant-resolver-header/instruments";

const instruments = createHeaderResolverInstruments();
instruments.resolvesTotal.add(1, { resolver: "header", outcome: "resolved" });

Usage: Advanced

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.