diff --git a/app/webrtc/metrics.go b/app/webrtc/metrics.go index 5df3aca..9197575 100644 --- a/app/webrtc/metrics.go +++ b/app/webrtc/metrics.go @@ -6,7 +6,6 @@ import ( corewebrtc "github.com/datarhei/core/v16/core/webrtc" coreprom "github.com/datarhei/core/v16/prometheus" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" ) var iceHistBuckets = []float64{0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10} @@ -21,43 +20,63 @@ type webrtcMetrics struct { ffmpegLegFailures *prometheus.CounterVec } +// mustRegisterCounter creates a CounterVec and registers it with reg. +// Panics on duplicate registration (same semantics as promauto). +func mustRegisterCounter(reg prometheus.Registerer, opts prometheus.CounterOpts, labels []string) *prometheus.CounterVec { + m := prometheus.NewCounterVec(opts, labels) + reg.MustRegister(m) + return m +} + +// mustRegisterHistogram creates a HistogramVec and registers it with reg. +func mustRegisterHistogram(reg prometheus.Registerer, opts prometheus.HistogramOpts, labels []string) *prometheus.HistogramVec { + m := prometheus.NewHistogramVec(opts, labels) + reg.MustRegister(m) + return m +} + func initMetrics(reg prometheus.Registerer, core string) *webrtcMetrics { - f := promauto.With(reg) cl := prometheus.Labels{"core": core} return &webrtcMetrics{ - whepRequests: f.NewCounterVec(prometheus.CounterOpts{ + whepRequests: mustRegisterCounter(reg, prometheus.CounterOpts{ Name: "dragonfork_webrtc_whep_requests_total", Help: "Count of WHEP HTTP requests by route, HTTP status code, and stream.", ConstLabels: cl, }, []string{"route", "code", "stream_id"}), - whepRequestDuration: f.NewHistogramVec(prometheus.HistogramOpts{ + + whepRequestDuration: mustRegisterHistogram(reg, prometheus.HistogramOpts{ Name: "dragonfork_webrtc_whep_request_duration_seconds", Help: "Server-side WHEP request latency in seconds, by route and stream.", ConstLabels: cl, Buckets: iceHistBuckets, }, []string{"route", "stream_id"}), - iceEstablishment: f.NewHistogramVec(prometheus.HistogramOpts{ + + iceEstablishment: mustRegisterHistogram(reg, prometheus.HistogramOpts{ Name: "dragonfork_webrtc_ice_establishment_duration_seconds", Help: "Duration from peer creation to first connected or failed ICE state.", ConstLabels: cl, Buckets: iceHistBuckets, }, []string{"stream_id", "result"}), - iceFailures: f.NewCounterVec(prometheus.CounterOpts{ + + iceFailures: mustRegisterCounter(reg, prometheus.CounterOpts{ Name: "dragonfork_webrtc_ice_failures_total", Help: "Count of ICE failures by stream and reason.", ConstLabels: cl, }, []string{"stream_id", "reason"}), - codecMismatches: f.NewCounterVec(prometheus.CounterOpts{ + + codecMismatches: mustRegisterCounter(reg, prometheus.CounterOpts{ Name: "dragonfork_webrtc_codec_mismatches_total", Help: "Count of 406 codec-mismatch rejections by stream and codec kind.", ConstLabels: cl, }, []string{"stream_id", "kind"}), - capRejections: f.NewCounterVec(prometheus.CounterOpts{ + + capRejections: mustRegisterCounter(reg, prometheus.CounterOpts{ Name: "dragonfork_webrtc_cap_rejections_total", Help: "Count of 503 peer-cap rejections by stream and scope (global or stream).", ConstLabels: cl, }, []string{"stream_id", "scope"}), - ffmpegLegFailures: f.NewCounterVec(prometheus.CounterOpts{ + + ffmpegLegFailures: mustRegisterCounter(reg, prometheus.CounterOpts{ Name: "dragonfork_webrtc_ffmpeg_leg_failures_total", Help: "Count of FFmpeg RTP output leg failures (process stopped while peers were active).", ConstLabels: cl, @@ -105,6 +124,6 @@ func (h *Handler) trackICE(streamID string, peer *corewebrtc.Peer, t0 time.Time) case <-peer.Done(): dur := time.Since(t0) h.met.iceEstablishment.WithLabelValues(streamID, "failed").Observe(dur.Seconds()) - h.met.iceFailures.WithLabelValues(streamID, "failed").Inc() + h.met.iceFailures.WithLabelValues(streamID, "reason").Inc() } }