3.2 KiB
otel/instruments
(no submodules)
Purpose
The otel/instruments module owns the OpenTelemetry instrument bundle
emitted by @suaveplan/api-plugin-campaigns. It binds a meter under the
"@suaveplan/api-plugin-campaigns" instrumentation-scope name against the
process-global MeterProvider and exposes a lazily-cached factory so the
plugin's route handlers, repositories, and lifecycle hooks share a single
counter / histogram pair without re-resolving the meter on every call.
The bundle is intentionally small in Chunk A: a request-count counter and a request-duration histogram covering every route mounted by the plugin. Chunks B and C extend it (in their own modules) with per-domain instruments for campaign writes, suppression checks, and webhook ingestion outcomes.
Features
- Lazy, cached meter binding — first call resolves the global meter; later calls return the cached bundle so handlers can record metrics in their hot path without provider lookup overhead.
resetCampaignsApiInstruments()hook for tests that install a freshMeterProviderafter the first cache write.- Counter + histogram primitives only; aggregation strategy and views are
owned by the host's
MeterProvider, not this package.
Usage: Basic
import { getCampaignsApiInstruments } from "@suaveplan/api-plugin-campaigns";
const { requestsTotal, requestDurationMs } = getCampaignsApiInstruments();
const start = performance.now();
try {
await handler();
requestsTotal.add(1, { route: "/campaigns", outcome: "ok" });
} catch (err) {
requestsTotal.add(1, { route: "/campaigns", outcome: "error" });
throw err;
} finally {
requestDurationMs.record(performance.now() - start, {
route: "/campaigns",
});
}
Usage: Advanced
import { metrics } from "@opentelemetry/api";
import { MeterProvider } from "@opentelemetry/sdk-metrics";
import {
getCampaignsApiInstruments,
resetCampaignsApiInstruments,
} from "@suaveplan/api-plugin-campaigns";
// Test set-up: rebind to a freshly-installed provider.
const provider = new MeterProvider({ readers: [] });
metrics.setGlobalMeterProvider(provider);
resetCampaignsApiInstruments();
// The next resolve binds to `provider`, not the implicit global from
// before the rebind.
const bundle = getCampaignsApiInstruments();
bundle.requestsTotal.add(1, { route: "/x", outcome: "ok" });
API Reference
interface CampaignsApiInstruments { readonly requestsTotal: Counter; readonly requestDurationMs: Histogram }getCampaignsApiInstruments(): CampaignsApiInstruments— cached factory.resetCampaignsApiInstruments(): void— drops the cache.
Implementation Notes
The cache is a module-level mutable binding. That is intentional and
matches the upstream pattern in @suaveplan/api-plugin-contacts: an
inert no-op metric is cheap to recreate, but creating one per request is
not, so caching plus an explicit reset hook for tests is the simplest
honest design.
The bundle is readonly so handlers cannot accidentally mutate it; the
cache itself is mutable but only the reset hook reaches it. Reset is
test-only — production code never invalidates the cache because the
global provider is installed once at process start.