# `instruments.ts` — webhook plugin OpenTelemetry instruments _(no submodules)_ ## Purpose Defines the four OpenTelemetry instruments emitted by the webhook plugin: two counters and two histograms covering every observable outcome of `WebhookService.send`. The bundle is lazily built against the process-wide `MeterProvider` and cached so repeat calls return the same `Counter` / `Histogram` references — this means tests can install a fresh provider, call `resetWebhookInstruments()`, and trust that the next `getWebhookInstruments()` rebinds against it. Without this seam, tests that install a `MeterProvider` after the first import see metrics flow to a stale meter and silently drop observations. ## Features - Four instruments — `deliveries_total`, `dead_letters_total`, `delivery_attempts`, `send_duration_ms`. - Lazy + cached binding so the meter is resolved on first read, not at import time. - `resetWebhookInstruments()` for test isolation. - Stable namespace `webhook_plugin.*` to keep the plugin's metrics separate from `@suaveplan/webhook-client`'s `webhook_client.*` metrics. ## Usage: Basic ```ts import { getWebhookInstruments } from "@suaveplan/api-plugin-webhook/otel/instruments"; const inst = getWebhookInstruments(); inst.deliveriesTotal.add(1, { event: "order.created", status: "delivered" }); ``` ## Usage: Advanced ```ts // Tests typically install a fresh MeterProvider, then call resetWebhookInstruments // so the next getWebhookInstruments() rebinds against the freshly-installed provider. import { metrics } from "@opentelemetry/api"; import { AggregationTemporality, InMemoryMetricExporter, MeterProvider, PeriodicExportingMetricReader, } from "@opentelemetry/sdk-metrics"; import { getWebhookInstruments, resetWebhookInstruments, } from "@suaveplan/api-plugin-webhook/otel/instruments"; const exporter = new InMemoryMetricExporter(AggregationTemporality.CUMULATIVE); const reader = new PeriodicExportingMetricReader({ exporter, exportIntervalMillis: 60_000 }); const provider = new MeterProvider({ readers: [reader] }); metrics.setGlobalMeterProvider(provider); resetWebhookInstruments(); const inst = getWebhookInstruments(); inst.deliveriesTotal.add(1, { event: "evt", status: "delivered" }); await provider.forceFlush(); console.log(exporter.getMetrics()); ``` ## API Reference - `getWebhookInstruments()` — returns the cached `WebhookInstruments` bundle, lazily binding on first call. - `resetWebhookInstruments()` — drops the cache so the next `getWebhookInstruments()` rebinds. - `WebhookInstruments` — `{ deliveriesTotal, deadLettersTotal, deliveryAttempts, sendDurationMs }`. ## Implementation Notes The namespace `webhook_plugin.*` was chosen explicitly so dashboards and alerts can distinguish plugin-level emissions from the `webhook_client.*` emissions the underlying delivery library emits. The label set `{event, status, endpoint_id}` on `deliveries_total` deliberately includes `endpoint_id` even though it has unbounded cardinality — operators with many endpoints SHOULD aggregate at scrape time using a recording rule that drops the label. Including it at emission time means operators can debug a single endpoint without recompiling. The histograms inherit OTel's default bucket layout (no custom `boundaries` supplied) — operators tune via SDK views, not by editing this file.