Files
skills/docs/media/audio/midi/src/telemetry/telemetry.md
2026-05-29 08:20:56 +01:00

3.1 KiB

telemetry/telemetry

(no submodules)

Purpose

OpenTelemetry-shaped instrumentation for @suaveplan/midi without taking a runtime dependency on @opentelemetry/api. The module exposes structural CounterLike / HistogramLike interfaces (compatible with the real OTel counter/histogram shapes) and a default in-memory recorder for tests and embedded use. Consumers wire real OTel by passing meter.createCounter(...) / meter.createHistogram(...) directly.

The two instruments the package emits are: midi_messages_decoded_total{type} (Counter) and midi_decode_duration_ns{type} (Histogram). Both carry the MIDI message type as a single attribute — enough to slice dashboards by note-on rate, SysEx frequency, decode-latency outliers per category, etc.

Features

  • Zero runtime dep on @opentelemetry/api — structural typing only.
  • createInMemoryTelemetry recorder for tests, with snapshot + reset.
  • nowNanos() cross-runtime high-resolution timestamp.
  • recordDecode(telemetry, type, startNs, endNs) one-call helper that stamps both counter and histogram with the message-type attribute.

Usage: Basic

import { createInMemoryTelemetry, nowNanos, recordDecode } from "@suaveplan/midi";

const telemetry = createInMemoryTelemetry();
const start = nowNanos();
// ...decode...
recordDecode(telemetry, "noteOn", start, nowNanos());

console.log(telemetry.snapshot().counters.noteOn);

Usage: Advanced

import type { MidiTelemetry } from "@suaveplan/midi";

// Bridge to real OpenTelemetry without a hard dep.
function createOtelTelemetry(meter: {
  createCounter(name: string): { add: (n: number, attrs?: object) => void };
  createHistogram(name: string): { record: (n: number, attrs?: object) => void };
}): MidiTelemetry {
  return {
    messagesDecoded: meter.createCounter("midi_messages_decoded_total"),
    decodeDurationNs: meter.createHistogram("midi_decode_duration_ns"),
  };
}

API Reference

  • CounterLike { add(value, attributes?): void }.
  • HistogramLike { record(value, attributes?): void }.
  • MidiTelemetry { messagesDecoded: CounterLike; decodeDurationNs: HistogramLike }.
  • createInMemoryTelemetry(): InMemoryTelemetry — extends MidiTelemetry with snapshot() and reset().
  • MidiTelemetrySnapshot { counters: Record<string, number>; histograms: Record<string, number[]> }.
  • nowNanos(): number — performance.now() * 1e6 when available; falls back to Date.now() * 1e6.
  • recordDecode(telemetry, type, startNs, endNs): void.
  • MIDI_TELEMETRY_NAMES — frozen object with the canonical instrument names.

Implementation Notes

The in-memory recorder uses Object.create(null) for the counter / histogram backing objects so accidental __proto__ keys cannot poison the snapshot. nowNanos multiplies milliseconds by 1_000_000 rather than using process.hrtime.bigint() because the latter is Node-only — the package is universal and must run identically in browsers. The slight precision loss (sub-microsecond) is acceptable for a coarse decode-duration histogram.