# `otel/instruments` _(no submodules)_ ## Purpose Registers the OpenTelemetry counter and histogram that every CLI command in `@suaveplan/server-keys` emits exactly once per invocation through the `runFunction` wrapper. The instruments live behind the global meter provider supplied by `@opentelemetry/api`, so production deployments can plug in any exporter (OTLP, Prometheus, Jaeger) without changing the package. Tests wire an in-memory `MeterProvider` in `tests/setup/polyfills.ts` before any source module is imported, so the meter resolved at load time is the real provider's meter, never the noop fallback. The `recordCommand(command, outcome, durationMs, errorCode?)` helper is the single call site every command goes through — keeping the attribute keys consistent and the histogram unit (`ms`) authoritative. ## Features - Counter `server_keys.command_total{command, command.outcome[, command.error_code]}`. - Histogram `server_keys.command_duration_ms{command, command.outcome[, command.error_code]}` with `unit: "ms"`. - Stable attribute-key constants (`ATTR_COMMAND`, `ATTR_OUTCOME`, `ATTR_ERROR_CODE`) so tests can assert by name. - `recordCommand` records both instruments atomically so dashboards built on the counter and the histogram never disagree. ## Usage: Basic ```ts import { recordCommand } from "@suaveplan/server-keys/otel/instruments"; const start = performance.now(); try { await doWork(); recordCommand("create-root-keys", "success", performance.now() - start); } catch (err) { recordCommand("create-root-keys", "error", performance.now() - start); throw err; } ``` ## Usage: Advanced Attach the typed error code when failing, so dashboards can break down failures by reason without scraping log lines: ```ts import { recordCommand } from "@suaveplan/server-keys/otel/instruments"; import { ServerKeysError } from "@suaveplan/server-keys/error-codes/error-codes"; try { await prepareFileCommand(...); recordCommand("prepare-file", "success", elapsed); } catch (err) { const code = err instanceof ServerKeysError ? err.code : "UNKNOWN"; recordCommand("prepare-file", "error", elapsed, code); throw err; } ``` Or query the in-memory test exporter to assert on emitted metrics: ```ts await flushTestMetrics(); // from tests/setup/polyfills.ts ``` ## API Reference - `commandTotal: Counter` — total CLI commands executed. - `commandDurationMs: Histogram` — per-command duration (ms). - `ATTR_COMMAND`, `ATTR_OUTCOME`, `ATTR_ERROR_CODE` — string-literal attribute keys. - `CommandOutcome = "success" | "error"`. - `recordCommand(command, outcome, durationMs, errorCode?)` — record one observation on both instruments. ## Implementation Notes `metrics.getMeter(name, version)` returns the meter from the current global provider; if no provider is registered, the API package returns a noop meter so production code never throws on missing telemetry. Setting the meter provider AFTER `instruments.ts` is imported has no effect on this module's meter handle, so the test polyfill must register the provider before any `import` that transitively loads `instruments.ts`.