first commit
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# `otel/metrics` — OpenTelemetry instrumentation
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## 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 `<domain>.<metric>` convention seen in the wider ecosystem; the histogram unit is set to `"ms"` so dashboards render correctly without a unit-conversion step.
|
||||
Reference in New Issue
Block a user