first commit
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
# telemetry
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
OpenTelemetry instrument bridge for the SpessaSynth plugin. Exposes three instruments under the meter `@suaveplan/audio-fx-plugin-spessasynth`: a monotonic counter `spessasynth_voices_triggered_total`, a histogram `spessasynth_process_duration_us`, and an observable gauge `spessasynth_voices_active`. The instruments are lazily created on first use and cached so the underlying OTel SDK sees a stable identity across the process lifetime.
|
||||
|
||||
The plugin core, voice manager, and MIDI router call into this module — none of those modules import `@opentelemetry/api` directly — so telemetry can be swapped or stubbed by mutating this single file.
|
||||
|
||||
## Features
|
||||
|
||||
- Lazy instrument creation: instruments are not registered with the SDK until first call.
|
||||
- `recordVoicesTriggered(n?)` — counter bump on every `noteOn`.
|
||||
- `recordProcessDuration(durationUs)` — histogram sample per `process()` block.
|
||||
- `setActiveVoices(n)` — latched value the observable gauge reads on each collection.
|
||||
- `getActiveVoicesValue()` — read-back for diagnostics and unit tests.
|
||||
- `resetForTesting()` — clears the singletons so a test suite that swaps the meter provider can re-register from scratch.
|
||||
- All instrument labels are cardinality-stable: no per-channel dimensions, no per-voice tags — chosen so a long-running synth session does not blow up the metric backend.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import {
|
||||
recordVoicesTriggered,
|
||||
recordProcessDuration,
|
||||
setActiveVoices,
|
||||
} from "@suaveplan/audio-fx-plugin-spessasynth/telemetry";
|
||||
|
||||
recordVoicesTriggered(1);
|
||||
recordProcessDuration(412); // microseconds
|
||||
setActiveVoices(processor.voiceCount);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { metrics } from "@opentelemetry/api";
|
||||
import { MeterProvider } from "@opentelemetry/sdk-metrics";
|
||||
import { InMemoryMetricExporter } from "@opentelemetry/sdk-metrics";
|
||||
import {
|
||||
resetForTesting,
|
||||
setActiveVoices,
|
||||
} from "@suaveplan/audio-fx-plugin-spessasynth/telemetry";
|
||||
|
||||
const exporter = new InMemoryMetricExporter(0);
|
||||
const provider = new MeterProvider({ readers: [/* periodic reader */] });
|
||||
metrics.setGlobalMeterProvider(provider);
|
||||
resetForTesting();
|
||||
|
||||
setActiveVoices(3);
|
||||
const collected = await exporter.collect();
|
||||
// `spessasynth_voices_active` gauge in collected.scopeMetrics → 3
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `recordVoicesTriggered(n?: number): void` — counter add, default 1.
|
||||
- `recordProcessDuration(durationUs: number): void` — histogram record.
|
||||
- `setActiveVoices(count: number): void` — latch the gauge value.
|
||||
- `getActiveVoicesValue(): number` — read the latched value.
|
||||
- `resetForTesting(): void` — clears singletons (test-only).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The observable gauge is a one-shot construction with a callback that reads the latched `_activeVoicesValue` module-level variable. The latched approach (rather than passing the processor in to read `voiceCount`) decouples the telemetry module from the upstream library and lets `setActiveVoices` be called from a high-frequency path (every `process()` block) without per-call overhead. `resetForTesting` deliberately drops references to the cached instruments — the OTel API gracefully re-creates them on the next call, but the gauge callback registration is lost; tests that need to re-arm the gauge should call `setActiveVoices` at least once after resetting.
|
||||
Reference in New Issue
Block a user