Files
suaveplan/packages/communication/emails/emailer/src/otel/metrics.md
2026-05-27 23:29:53 +01:00

2.7 KiB

metrics.ts

(no submodules)

Purpose

Single source of truth for every OpenTelemetry instrument emitted by @suaveplan/emailer. Every production path that touches the wire — send attempts, send failures, latency, retries, queue enqueues — records exactly one observation through one of the helpers exported here. Centralising the emission lets the spec-conformance test grep one file to assert that every declared instrument is wired into a production call.

Features

  • recordSend(transport, outcome, latencyMs) — counter + histogram pair for every send attempt.
  • recordSendError(transport, code) — counter keyed on the typed EmailerErrorCode so dashboards filter by code path, not message.
  • recordRetry(transport) — counter on every retry attempt.
  • recordQueue(transport) — counter on every enqueue.
  • refreshEmailerMeters() — re-binds every instrument against the current global MeterProvider; tests call this after installing an in-memory provider so subsequent emissions land on the test exporter.

Usage: Basic

import { recordSend } from "@suaveplan/emailer/otel/metrics";

const startedAt = Date.now();
// ... actual send ...
recordSend("dummy", "ok", Date.now() - startedAt);

Usage: Advanced

import { MeterProvider } from "@opentelemetry/sdk-metrics";
import { metrics } from "@opentelemetry/api";
import { refreshEmailerMeters, recordSend } from "@suaveplan/emailer/otel/metrics";

const provider = new MeterProvider();
metrics.setGlobalMeterProvider(provider);
refreshEmailerMeters();

recordSend("dummy", "ok", 12);
// `provider.collect()` now contains an `emailer.send.total` observation
// with attributes `{ transport: "dummy", outcome: "ok" }`.

API Reference

  • refreshEmailerMeters(): void
  • recordSend(transport: string, outcome: "ok" | "error", latencyMs: number): void
  • recordSendError(transport: string, code: string): void
  • recordRetry(transport: string): void
  • recordQueue(transport: string): void

Implementation Notes

  • The five instrument names — emailer.send.total, emailer.send.errors, emailer.send.latency_ms, emailer.retry.total, emailer.queue.depth — are part of the public surface. Renaming them silently breaks every downstream dashboard, so any change must move through OpenSpec.
  • refreshEmailerMeters is required because the OpenTelemetry SDK does not propagate provider changes to existing instruments. Tests that swap the global provider must call it before invoking the production path, otherwise observations land on the module-load meter and the test sees zero data.
  • All instruments are universally available — the helpers carry no IO and are safe to call from synchronous hot paths.