first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
# `otel/metrics`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`otel/metrics` is the OpenTelemetry instrumentation layer for
`@suaveplan/sms`. It owns four meter instruments — three
counters and a histogram — that `SmsService` populates on every
send, retry, and failure. Wiring observability at the package
level keeps consumers free from re-defining instrument names and
units, and gives downstream dashboards a stable contract to plot
against.
The module reads the global `MeterProvider` from
`@opentelemetry/api`. Tests that wire an in-memory exporter call
`refreshSmsMeters()` after installing the exporter so subsequent
`record*` calls land on the test provider rather than the
load-time meter.
## Features
- `sms.sent_total{channel, from_me}` counter — successful sends.
- `sms.failed_total{code}` counter — failed sends, tagged by
error code.
- `sms.retry_total{attempt}` counter — retry attempts, tagged
by zero-based attempt index.
- `sms.send_duration_ms{outcome}` histogram — pipeline duration
in milliseconds, outcome ∈ {`ok`, `error`, `aborted`}.
- `refreshSmsMeters()` re-resolves every instrument against the
current `MeterProvider` for test wiring.
## Usage: Basic
```ts
import {
recordSmsSent,
recordSmsFailed,
recordSmsRetry,
recordSmsSendDuration,
} from "@suaveplan/sms/otel";
const start = Date.now();
try {
// ... send ...
recordSmsSent("marketing", false);
recordSmsSendDuration(Date.now() - start, "ok");
} catch (err) {
recordSmsFailed("SMS.SEND_FAILED");
recordSmsSendDuration(Date.now() - start, "error");
}
```
## Usage: Advanced
```ts
import {
MeterProvider,
PeriodicExportingMetricReader,
ConsoleMetricExporter,
} from "@opentelemetry/sdk-metrics";
import { metrics } from "@opentelemetry/api";
import { refreshSmsMeters } from "@suaveplan/sms/otel";
const provider = new MeterProvider({
readers: [
new PeriodicExportingMetricReader({
exporter: new ConsoleMetricExporter(),
}),
],
});
metrics.setGlobalMeterProvider(provider);
refreshSmsMeters(); // rebind every instrument against the new provider
```
## API Reference
- `function recordSmsSent(channel: string, fromMe: boolean): void`
- `function recordSmsFailed(code: string): void`
- `function recordSmsRetry(attempt: number): void`
- `function recordSmsSendDuration(durationMs: number, outcome:
"ok" | "error" | "aborted"): void`
- `function refreshSmsMeters(): void`
## Implementation Notes
Instruments are stored at module scope so the hot send path
performs zero allocation per record call. The default no-op
meter provided by `@opentelemetry/api` accepts every call
silently, so packages that never install a `MeterProvider` pay
nothing. Calling `refreshSmsMeters()` re-creates each instrument
against the current global — necessary because instrument
identity is bound to the meter instance, not the API surface.
The attribute keys (`channel`, `from_me`, `code`, `attempt`,
`outcome`) are part of the public observability contract; renames
require a major version bump.