Files
suaveplan/packages/ui/react/react-audio-stream/src/telemetry/telemetry.md
2026-05-27 23:47:53 +01:00

3.3 KiB

telemetry

(no submodules)

Purpose

The telemetry module owns every OpenTelemetry instrument published by @suaveplan/react-audio-stream. Concentrating the OTel surface in one file means the session class never touches @opentelemetry/api and tests can verify call counts by spying on a single verb-shaped object instead of low-level meter handles. The shape matches Requirement 7 of the parent spec — two counters, a gauge, two more counters, and a histogram — so the runtime instrument set can be cross-checked against the spec on every change.

Every emission is no-op until a host registers an OTel SDK provider via @suaveplan/opentelemetry. The module-scope meter handles are cheap (one map lookup) so we can keep them at file scope without inflating cold-start cost.

Features

  • Counter react_audio_stream_peer_connect_total{transport}.
  • Counter react_audio_stream_peer_disconnect_total{reason}, where reason covers every InterruptionClass plus explicit.
  • UpDown counter react_audio_stream_active_sessions (gauge-equivalent via OTel's synchronous up/down counter).
  • Counter react_audio_stream_reconnect_attempt_total.
  • Counter react_audio_stream_reconnect_failed_total.
  • Histogram react_audio_stream_rtt_ms{participant} for the transport's RTT probe.
  • Verbs (peerConnect, peerDisconnect, …) keep call sites tidy and give tests one knob per spec requirement.

Usage: Basic

import { streamTelemetry } from "@suaveplan/react-audio-stream";

streamTelemetry.peerConnect("webrtc");
streamTelemetry.recordRtt("alice", 42);

Usage: Advanced

import { streamTelemetry } from "@suaveplan/react-audio-stream";

class ReconnectLadder {
  async run(verdict: InterruptionVerdict): Promise<void> {
    for (let attempt = 1; attempt <= this.max; attempt++) {
      streamTelemetry.reconnectAttempt();
      try {
        await this.session.connect(this.roomId);
        return;
      } catch {
        await this.backoff(attempt);
      }
    }
    streamTelemetry.reconnectFailed();
  }
}

API Reference

  • interface StreamTelemetry — verb surface consumed by the session.
  • const streamTelemetry: StreamTelemetry — singleton implementation.
  • type DisconnectReason — "network" | "media-permission" | "signalling" | "peer" | "unknown" | "explicit".
  • type TransportLabel — "webrtc" | "websocket" | "trystero" | "local".

Implementation Notes

OTel's Gauge requires async observable callbacks which complicates the testable verb surface; the spec wording reads as "gauge tracks active sessions" and OTel's UpDownCounter is the synchronous equivalent that satisfies the same semantics — every monitoring backend renders it as a gauge. The reason labels are constrained to the DisconnectReason union so a typo in the call site is a compile error rather than a silent cardinality leak. Transport labels are constrained the same way — adding a new transport in chunk B requires extending the union (one line) so backend dashboards stay in sync with the code automatically. The participant id is used as a histogram attribute even though it is high-cardinality because RTT samples are per-peer by nature; consumers wiring a low-cardinality backend (Prometheus) should drop the attribute via a view in their SDK configuration rather than us removing it here.