From 23636e4a76c9b4a3299730804c2344dccbc15f50 Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Wed, 6 May 2026 15:56:27 -0400 Subject: [PATCH] feat(prometheus): add WebRTC snapshot collector --- prometheus/webrtc.go | 75 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 prometheus/webrtc.go diff --git a/prometheus/webrtc.go b/prometheus/webrtc.go new file mode 100644 index 0000000..a834d6c --- /dev/null +++ b/prometheus/webrtc.go @@ -0,0 +1,75 @@ +package prometheus + +import ( + // Hybrid instrumentation rationale: direct client_golang instrumentation + // lives in app/webrtc/metrics.go (hot-path counters and histograms); + // this file owns the snapshot-style gauges. See the design doc at + // docs/design/2026-05-03-datarhei-dragon-fork-webrtc-prometheus-metrics-design.md. + "github.com/prometheus/client_golang/prometheus" +) + +// WebRTCStats is a point-in-time snapshot of the WebRTC subsystem state. +// Populated by Handler.Stats() in app/webrtc and consumed by the collector +// at scrape time. +type WebRTCStats struct { + StreamCount int + PeersByStream map[string]int + UDPPortsInUse int +} + +// WebRTCStatsSource is implemented by app/webrtc.Handler (via its Stats() +// method). The interface lives here so prometheus/ does not import app/webrtc, +// keeping the dependency arrow one-directional. +type WebRTCStatsSource interface { + Stats() WebRTCStats +} + +type webrtcCollector struct { + core string + source WebRTCStatsSource + + activeStreamsDesc *prometheus.Desc + activePeersDesc *prometheus.Desc + udpPortsInUseDesc *prometheus.Desc +} + +// NewWebRTCCollector returns a prometheus.Collector that emits three gauge +// metrics at scrape time by calling source.Stats(). Register it with the +// shared Metrics registry before the /metrics endpoint starts serving. +func NewWebRTCCollector(core string, source WebRTCStatsSource) prometheus.Collector { + cl := prometheus.Labels{"core": core} + return &webrtcCollector{ + core: core, + source: source, + activeStreamsDesc: prometheus.NewDesc( + "dragonfork_webrtc_active_streams", + "Streams currently registered (processes with webrtc.enabled=true running).", + nil, cl, + ), + activePeersDesc: prometheus.NewDesc( + "dragonfork_webrtc_active_peers", + "Currently subscribed WHEP peers per stream.", + []string{"stream_id"}, cl, + ), + udpPortsInUseDesc: prometheus.NewDesc( + "dragonfork_webrtc_udp_ports_in_use", + "UDP ports currently allocated (2 per active stream).", + nil, cl, + ), + } +} + +func (c *webrtcCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- c.activeStreamsDesc + ch <- c.activePeersDesc + ch <- c.udpPortsInUseDesc +} + +func (c *webrtcCollector) Collect(ch chan<- prometheus.Metric) { + stats := c.source.Stats() + ch <- prometheus.MustNewConstMetric(c.activeStreamsDesc, prometheus.GaugeValue, float64(stats.StreamCount)) + ch <- prometheus.MustNewConstMetric(c.udpPortsInUseDesc, prometheus.GaugeValue, float64(stats.UDPPortsInUse)) + for streamID, count := range stats.PeersByStream { + ch <- prometheus.MustNewConstMetric(c.activePeersDesc, prometheus.GaugeValue, float64(count), streamID) + } +}