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,91 @@
# `otel/instruments` — OpenTelemetry instruments
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Builds the OpenTelemetry instrument bundle emitted by the canonical
`ImportService` so consumers can wire production observability without
hand-rolling counter names. The bundle is a single cached object keyed
against the global `MeterProvider` — the first `getImportInstruments()`
call resolves the meter; subsequent calls return the same reference.
Tests rebind by installing their own `MeterProvider`, calling
`resetImportInstruments()`, and reading back via an `InMemoryMetricExporter`.
## Features
- Nine instruments: `jobsTotal`, `rowsTotal`, `rowErrorsTotal`,
`durationMs` (histogram), `storeWritesTotal`, `storeReadsTotal`,
`sseConnected`, `sseEventsSent`, `sseDisconnected`.
- `storeWritesTotal{outcome}` and `storeReadsTotal{outcome}` are emitted
by `SurrealImportStore` on every adapter call so dashboards can split
backend health from upstream import success/failure. The in-memory
adapter (`MemoryImportStore`) does not emit; durable backends do.
- `sseConnected`, `sseEventsSent{kind}`, and `sseDisconnected{reason}`
are emitted by `GET /imports/:id/progress` so dashboards can chart
open SSE connections, frame throughput by event kind, and teardown
causes (terminal `complete` / `failed` vs client abort).
- Cached bundle — production code does not re-resolve the meter on
every emit.
- `resetImportInstruments()` drops the cache so a freshly-installed
`MeterProvider` rebinds on the next call.
- Asserted by `instruments.test.ts` against a real in-memory exporter
(no mocks).
## Usage: Basic Example
```ts
import { getImportInstruments } from "@suaveplan/api-plugin-import";
const instruments = getImportInstruments();
instruments.jobsTotal.add(1, { outcome: "success" });
instruments.rowsTotal.add(42, { outcome: "success" });
instruments.durationMs.record(123);
```
## Usage: Advanced Example
```ts
import { metrics } from "@opentelemetry/api";
import {
AggregationTemporality,
InMemoryMetricExporter,
MeterProvider,
PeriodicExportingMetricReader,
} from "@opentelemetry/sdk-metrics";
import { getImportInstruments, resetImportInstruments } from "@suaveplan/api-plugin-import";
const exporter = new InMemoryMetricExporter(AggregationTemporality.CUMULATIVE);
const reader = new PeriodicExportingMetricReader({ exporter, exportIntervalMillis: 60_000 });
const provider = new MeterProvider({ readers: [reader] });
metrics.setGlobalMeterProvider(provider);
resetImportInstruments();
const instruments = getImportInstruments();
instruments.jobsTotal.add(1, { outcome: "success" });
await provider.forceFlush();
const collected = exporter.getMetrics();
// collected[0].scopeMetrics[0].metrics — read back the counter datapoint
```
## API Reference
- `interface ImportInstruments { jobsTotal: Counter; rowsTotal: Counter; rowErrorsTotal: Counter; durationMs: Histogram; storeWritesTotal: Counter; storeReadsTotal: Counter; sseConnected: Counter; sseEventsSent: Counter; sseDisconnected: Counter }`
- `getImportInstruments(): ImportInstruments`
- `resetImportInstruments(): void`
## Implementation Notes
- The meter is named `@suaveplan/api-plugin-import` — matching the
package name keeps cross-package metric reports readable.
- Counters carry the `outcome` label so dashboards can split jobs/rows
by success vs error without re-aggregating.
- The histogram unit is set to `"ms"` so OTel collectors that respect
unit annotation produce labelled charts; consumers MUST emit
millisecond values.
- The cache is module-scoped — every consumer of the package shares the
same instrument set. This is the desired behaviour because the meter
resolves against the global `MeterProvider`, which is a process-wide
singleton.