Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
# `otel` — host instrument registry
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`createHostInstruments` builds the frozen bundle of every OpenTelemetry counter and histogram the host emits on a production path. The instrument list mirrors design doc §12.3 exactly — every name is a published surface and removing one is a breaking change. The package never installs an OTel SDK; the meter-provider source is, in priority order, the explicit `meterProvider` override and then the OTel global provider (which falls back to a no-op when no SDK is wired). This keeps SDK initialisation an operator concern, not a library concern.
The registry also vends per-plugin meters via `forPlugin(name)` — each call returns a cached meter namespaced under `@suaveplan/api-host/plugin/<name>` with the plugin label pre-attached. Stage-4 packages plug their plugin-declared `manifest.telemetry` instruments onto these meters.
## Features
- Twelve counters (`HOST_COUNTER_NAMES`) and four histograms (`HOST_HISTOGRAM_NAMES`) covering the full §12.3 metric surface.
- Pre-tagged host meter with `host.id` + `host.version` attributes.
- Histogram units (`ms`, `By`) set at construction time.
- Cached per-plugin meters with `plugin.name` pre-attached.
- `tenantLabelOptIn` flag — defaults to `false` to bound cardinality.
- `dispose()` clears the per-plugin meter cache for graceful teardown.
## Usage: Basic
```ts
import { createHostInstruments } from "@suaveplan/api-host/otel";
const instruments = createHostInstruments({
hostId: "host-1",
hostVersion: "0.1.0",
});
instruments.counters["api.requests_total"].add(1, { route: "/billing/invoices" });
instruments.histograms["api.request_duration_ms"].record(42, {
route: "/billing/invoices",
});
```
## Usage: Advanced
Plug an explicit meter-provider (test harness or operator-supplied SDK):
```ts
import {
HOST_COUNTER_NAMES,
createHostInstruments,
} from "@suaveplan/api-host/otel";
import { MeterProvider } from "@opentelemetry/sdk-metrics";
const provider = new MeterProvider({
/* readers / views configured by the operator */
});
const instruments = createHostInstruments({
hostId: "host-1",
hostVersion: "0.1.0",
tenantLabelOptIn: true,
meterProvider: provider,
});
// Per-plugin meter — caches per `pluginName`.
const billingMeter = instruments.forPlugin("billing").meter;
const errorsByReason = billingMeter.createCounter("billing.errors_total");
// Verify the surface is complete.
for (const name of HOST_COUNTER_NAMES) {
instruments.counters[name]; // present.
}
```
## API Reference
- `createHostInstruments(config: HostInstrumentsConfig): HostInstruments`
- `HOST_COUNTER_NAMES` — `readonly` tuple of every counter name.
- `HOST_HISTOGRAM_NAMES` — `readonly` tuple of every histogram name.
- `HostInstruments`:
- `meter: Meter`
- `tenantLabelOptIn: boolean`
- `counters: Readonly<Record<HostCounterName, Counter>>`
- `histograms: Readonly<Record<HostHistogramName, Histogram>>`
- `forPlugin(name): PluginInstruments`
- `dispose(): void`
- `PluginInstruments`: `{ meter: Meter; pluginName: string }`.
## Implementation Notes
The counter / histogram lists are exported as `readonly` tuples so consumers can iterate and assert against them in tests — a missing instrument shows up as a test failure, not a runtime no-op. Per-plugin meters are cached by name to avoid leaking meter objects on every reload (one meter per plugin is the design invariant — disposal happens at uninstall, mirrored by `dispose()` here clearing the whole cache). Tenant-label opt-in is gated at the call site rather than the registry level: the registry exposes the flag and the request pipeline (Chunk B) decides whether to attach `tenant` to a given measurement. This keeps the cardinality story explicit at every emit site rather than hidden behind a registry policy.