2.7 KiB
otel — OpenTelemetry instrumentation for OPFS storage
(no submodules)
Purpose
Centralises every metric, histogram, and span emitted by OpfsStorage behind a single lazy instrument factory. Lazy construction lets consumers install a custom global MeterProvider / TracerProvider before the storage path executes, and lets tests pin a real in-memory MeterProvider against which the recorded attribute set is asserted. The module exposes a small recordOp({op, outcome, durationMs, bytes?, direction?}) helper that every operation in the storage class calls once.
Features
- Six instruments: 4 counters (
reads_total,writes_total,deletes_total,errors_total) and 2 histograms (op_duration_ms,io_bytes). - Stable label space:
{op, outcome}on every counter / duration,{op, direction}onio_bytes. - Aborted operations count toward
errors_totalwithoutcome: "aborted"so SLO dashboards can split them out. setStorageBrowserOpfsMeterProvider(provider)for runtime provider injection (used by@suaveplan/opentelemetry).resetInstruments()rebuilds the cache between test blocks.
Usage: Basic Example
import { recordOp } from "@suaveplan/storage-browser-opfs/otel";
recordOp({ op: "set", outcome: "success", durationMs: 12, bytes: 100, direction: "write" });
Usage: Advanced Example
import { metrics } from "@opentelemetry/api";
import {
getInstruments,
setStorageBrowserOpfsMeterProvider,
resetInstruments,
} from "@suaveplan/storage-browser-opfs/otel";
// In a Bun/Node test:
class CapturingMeterProvider { /* real in-memory MeterProvider */ }
const provider = new CapturingMeterProvider();
setStorageBrowserOpfsMeterProvider(provider);
resetInstruments();
const { writesTotal } = getInstruments(true);
writesTotal.add(1, { op: "set", outcome: "success" });
API Reference
setStorageBrowserOpfsMeterProvider(provider | undefined): void.getInstruments(force?: boolean): StorageBrowserOpfsInstruments— cached unlessforceistrue.recordOp(input: RecordOpInput): void.resetInstruments(): void.- Types:
StorageBrowserOpfsOp(14 op names),StorageBrowserOpfsOpOutcome(success | error | aborted),RecordOpInput.
Implementation Notes
The dispatch in recordOp is intentionally a flat if/else chain rather than a lookup table: TypeScript can statically verify exhaustiveness over StorageBrowserOpfsOp, and the cost of a 14-arm chain is negligible compared to the underlying counter add. aborted outcomes are routed to errors_total (not a separate counter) on purpose — operationally they're failures from the caller's perspective even though they're not exceptions on the server side.