From b5c847bad6cfcf39afd49c7dd643239ce92f346e Mon Sep 17 00:00:00 2001 From: Alessandro Ros Date: Sat, 26 Jul 2025 16:51:37 +0200 Subject: [PATCH] metrics: add paths_readers metric (#4473) (#4789) --- README.md | 1 + internal/core/metrics_test.go | 6 +++ internal/metrics/metrics.go | 1 + internal/metrics/metrics_test.go | 76 ++++++++++++++++++++++++++++++-- 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 910eef56..1cc17817 100644 --- a/README.md +++ b/README.md @@ -2135,6 +2135,7 @@ Obtaining: paths{name="[path_name]",state="[state]"} 1 paths_bytes_received{name="[path_name]",state="[state]"} 1234 paths_bytes_sent{name="[path_name]",state="[state]"} 1234 +paths_readers{name="[path_name]",state="[state]"} 1234 # metrics of every HLS muxer hls_muxers{name="[name]"} 1 diff --git a/internal/core/metrics_test.go b/internal/core/metrics_test.go index 96c82967..bcf2bb7d 100644 --- a/internal/core/metrics_test.go +++ b/internal/core/metrics_test.go @@ -336,21 +336,27 @@ webrtc_sessions_bytes_sent 0 `^paths\{name=".*?",state="ready"\} 1`+"\n"+ `paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+ + `paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths\{name=".*?",state="ready"\} 1`+"\n"+ `paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+ + `paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths\{name=".*?",state="ready"\} 1`+"\n"+ `paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+ + `paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths\{name=".*?",state="ready"\} 1`+"\n"+ `paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+ + `paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths\{name=".*?",state="ready"\} 1`+"\n"+ `paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+ + `paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths\{name=".*?",state="ready"\} 1`+"\n"+ `paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+ + `paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `hls_muxers\{name=".*?"\} 1`+"\n"+ `hls_muxers_bytes_sent\{name=".*?"\} 0`+"\n"+ `hls_muxers\{name=".*?"\} 1`+"\n"+ diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go index a20e975e..9f29f646 100644 --- a/internal/metrics/metrics.go +++ b/internal/metrics/metrics.go @@ -162,6 +162,7 @@ func (m *Metrics) onMetrics(ctx *gin.Context) { out += metric("paths", tags, 1) out += metric("paths_bytes_received", tags, int64(i.BytesReceived)) out += metric("paths_bytes_sent", tags, int64(i.BytesSent)) + out += metric("paths_readers", tags, int64(len(i.Readers))) } } else { out += metric("paths", "", 0) diff --git a/internal/metrics/metrics_test.go b/internal/metrics/metrics_test.go index ef5611e1..c62d75dc 100644 --- a/internal/metrics/metrics_test.go +++ b/internal/metrics/metrics_test.go @@ -7,21 +7,58 @@ import ( "time" "github.com/bluenviron/mediamtx/internal/conf" + "github.com/bluenviron/mediamtx/internal/defs" "github.com/bluenviron/mediamtx/internal/test" "github.com/stretchr/testify/require" ) +func timePtr(t time.Time) *time.Time { + return &t +} + +type dummyPathManager struct{} + +func (dummyPathManager) APIPathsList() (*defs.APIPathList, error) { + return &defs.APIPathList{ + ItemCount: 20, + PageCount: 1, + Items: []*defs.APIPath{{ + Name: "mypath", + ConfName: "mypathconf", + Source: &defs.APIPathSourceOrReader{ + Type: "testing", + ID: "123324354", + }, + Ready: true, + ReadyTime: timePtr(time.Date(2003, 11, 4, 23, 15, 7, 0, time.UTC)), + Tracks: []string{"H264", "H265"}, + BytesReceived: 123, + BytesSent: 456, + Readers: []defs.APIPathSourceOrReader{ + { + Type: "testing", + ID: "345234423", + }, + }, + }}, + }, nil +} + +func (dummyPathManager) APIPathsGet(string) (*defs.APIPath, error) { + panic("unused") +} + func TestPreflightRequest(t *testing.T) { - api := Metrics{ + m := Metrics{ Address: "localhost:9998", AllowOrigin: "*", ReadTimeout: conf.Duration(10 * time.Second), AuthManager: test.NilAuthManager, Parent: test.NilLogger, } - err := api.Initialize() + err := m.Initialize() require.NoError(t, err) - defer api.Close() + defer m.Close() tr := &http.Transport{} defer tr.CloseIdleConnections() @@ -47,3 +84,36 @@ func TestPreflightRequest(t *testing.T) { require.Equal(t, "Authorization", res.Header.Get("Access-Control-Allow-Headers")) require.Equal(t, byts, []byte{}) } + +func TestMetrics(t *testing.T) { + m := Metrics{ + Address: "localhost:9998", + AllowOrigin: "*", + ReadTimeout: conf.Duration(10 * time.Second), + AuthManager: test.NilAuthManager, + Parent: test.NilLogger, + } + err := m.Initialize() + require.NoError(t, err) + defer m.Close() + + m.SetPathManager(&dummyPathManager{}) + + tr := &http.Transport{} + defer tr.CloseIdleConnections() + hc := &http.Client{Transport: tr} + + res, err := hc.Get("http://localhost:9998/metrics") + require.NoError(t, err) + defer res.Body.Close() + + byts, err := io.ReadAll(res.Body) + require.NoError(t, err) + + require.Equal(t, + `paths{name="mypath",state="ready"} 1`+"\n"+ + `paths_bytes_received{name="mypath",state="ready"} 123`+"\n"+ + `paths_bytes_sent{name="mypath",state="ready"} 456`+"\n"+ + `paths_readers{name="mypath",state="ready"} 1`+"\n", + string(byts)) +}