first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
# telemetry/telemetry
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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
```ts
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
```ts
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.