# `otel/metrics` — OpenTelemetry instrumentation _(no submodules)_ ## Purpose `metrics.ts` registers four OpenTelemetry instruments under the meter name `@suaveplan/slack-client` and exports the helper functions every production path uses to record observations: `recordRequest`, `recordError`, `recordWebhook`. The module intentionally retrieves the meter once at module load AND exposes `refreshSlackMeters()` so tests can rewire the meter against a real in-memory `MeterProvider` after configuring the global provider. This keeps the production hot path free of `MeterProvider` lookups while still permitting `getMetrics()` assertions in the test suite. ## Features - Four instruments: `slack.requests` (Counter), `slack.errors` (Counter), `slack.latency_ms` (Histogram, unit `ms`), `slack.webhook_sends` (Counter). - Attributes — `{ method, outcome }` on requests, `{ method, code }` on errors, `{ outcome }` on webhook sends. - Module-load resolution against `metrics.getMeter("@suaveplan/slack-client", "0.1.0")`. - `refreshSlackMeters()` for tests to rebind after `metrics.setGlobalMeterProvider(...)`. ## Usage: Basic ```ts import { recordRequest } from "@suaveplan/slack-client/otel/metrics"; recordRequest("chat.postMessage", "ok", 142); ``` ## Usage: Advanced ```ts import { metrics } from "@opentelemetry/api"; import { AggregationTemporality, InMemoryMetricExporter, MeterProvider, PeriodicExportingMetricReader, } from "@opentelemetry/sdk-metrics"; import { refreshSlackMeters } from "@suaveplan/slack-client/otel/metrics"; const exporter = new InMemoryMetricExporter(AggregationTemporality.CUMULATIVE); const provider = new MeterProvider({ readers: [ new PeriodicExportingMetricReader({ exporter, exportIntervalMillis: 60_000, }), ], }); metrics.setGlobalMeterProvider(provider); refreshSlackMeters(); // ... run code that records ... await provider.forceFlush(); const observed = exporter.getMetrics(); ``` ## API Reference - `recordRequest(method: string, outcome: "ok" | "error", latencyMs: number): void` — increments `slack.requests` and records `slack.latency_ms`. - `recordError(method: string, code: string): void` — increments `slack.errors`. - `recordWebhook(outcome: "ok" | "error"): void` — increments `slack.webhook_sends`. - `refreshSlackMeters(): void` — re-resolves every instrument against the current global meter provider. ## Implementation Notes The instruments are held in module-scope `let` bindings (not `const`) because `refreshSlackMeters()` rebinds them after a test wires a fresh `MeterProvider`. This is the standard pattern for packages that exercise OTel in tests via a real in-memory exporter — the alternative (proxying every call through a getter) adds a per-call indirection on the production hot path. Instrument names follow the `.` convention seen in the wider ecosystem; the histogram unit is set to `"ms"` so dashboards render correctly without a unit-conversion step.