# telemetry _(no submodules)_ ## Purpose Provides the shared OpenTelemetry meter and instrument set used by every public entry point of `@suaveplan/email-processing`. Instruments are constructed lazily on first call, then cached so each metric is bound exactly once per process — this is what makes the package's spec-conformance test (which runs against a real `MeterProvider` + `InMemoryMetricExporter`) deterministic. There are no mocks: the test setup installs a real meter provider before any source module imports. ## Features - Lazy, idempotent singleton (`getEmailProcessingInstruments`) with an explicit test-only `resetEmailProcessingInstruments` for re-binding after a fresh meter provider is installed in setup. - Stable instrument names under the `email_processing.*` prefix. - Six counters and one histogram covering parse counts, parse failures, attachment outcomes, attachment extraction duration, hook invocations, DKIM/SPF/DMARC outcomes, spam classification counts, and thread grouping counts. - Strictly typed `EmailProcessingInstruments` interface — counter misuses become typecheck errors at the call site. ## Usage: Basic ```ts import { getEmailProcessingInstruments } from "@suaveplan/email-processing/otel"; const m = getEmailProcessingInstruments(); m.parseCounter.add(1); ``` ## Usage: Advanced ```ts import { getEmailProcessingInstruments, resetEmailProcessingInstruments, } from "@suaveplan/email-processing/otel"; // In a test setup file: resetEmailProcessingInstruments(); const meter = getEmailProcessingInstruments(); const start = performance.now(); await processAttachments(parsed.attachments); meter.attachmentExtractionDurationMs.record(performance.now() - start, { "attachment.count": parsed.attachments.length, }); ``` ## API Reference - `EmailProcessingInstruments` — readonly interface listing every counter and histogram, plus the underlying `meter`. - `getEmailProcessingInstruments(): EmailProcessingInstruments` — returns the cached instrument set, building it on first call. - `resetEmailProcessingInstruments(): void` — clears the cache so the next `getEmailProcessingInstruments()` rebuilds against whatever `MeterProvider` is currently registered via `metrics.setGlobal*`. ## Implementation Notes The instruments are constructed against `metrics.getMeter(...)`, which defers to whichever `MeterProvider` was last installed via `metrics.setGlobalMeterProvider`. Tests install an `InMemoryMetricExporter`-backed provider in `tests/setup/polyfills.ts` before any source module loads; production code installs a real exporter via the application bootstrap. The histogram unit is `"ms"` and intentionally simple: the package emits durations in milliseconds from `performance.now()` deltas, so dashboards can read directly without unit conversion.