Initial Commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user