From 79872cabd6f0f6c4c58839884bf3608779450be9 Mon Sep 17 00:00:00 2001 From: Alessandro Ros Date: Tue, 11 Apr 2023 20:47:29 +0200 Subject: [PATCH] metrics: return metrics even if there are no paths or clients (#1688) --- internal/core/metrics.go | 93 +++++++++++++++++++++++------------ internal/core/metrics_test.go | 59 ++++++++++++++++++---- 2 files changed, 111 insertions(+), 41 deletions(-) diff --git a/internal/core/metrics.go b/internal/core/metrics.go index 9319cbed..49db96ee 100644 --- a/internal/core/metrics.go +++ b/internal/core/metrics.go @@ -16,8 +16,8 @@ import ( "github.com/aler9/mediamtx/internal/logger" ) -func metric(key string, value int64) string { - return key + " " + strconv.FormatInt(value, 10) + "\n" +func metric(key string, tags string, value int64) string { + return key + tags + " " + strconv.FormatInt(value, 10) + "\n" } type metricsParent interface { @@ -87,7 +87,7 @@ func (m *metrics) onMetrics(ctx *gin.Context) { out := "" res := m.pathManager.apiPathsList() - if res.err == nil { + if res.err == nil && len(res.data.Items) != 0 { for name, i := range res.data.Items { var state string if i.SourceReady { @@ -97,44 +97,57 @@ func (m *metrics) onMetrics(ctx *gin.Context) { } tags := "{name=\"" + name + "\",state=\"" + state + "\"}" - out += metric("paths"+tags, 1) - out += metric("paths_bytes_received"+tags, int64(i.BytesReceived)) + out += metric("paths", tags, 1) + out += metric("paths_bytes_received", tags, int64(i.BytesReceived)) } + } else { + out += metric("paths", "", 0) } if !interfaceIsEmpty(m.hlsServer) { res := m.hlsServer.apiMuxersList() - if res.err == nil { + if res.err == nil && len(res.data.Items) != 0 { for name, i := range res.data.Items { tags := "{name=\"" + name + "\"}" - out += metric("hls_muxers"+tags, 1) - out += metric("hls_muxers_bytes_sent"+tags, int64(i.BytesSent)) + out += metric("hls_muxers", tags, 1) + out += metric("hls_muxers_bytes_sent", tags, int64(i.BytesSent)) } + } else { + out += metric("hls_muxers", "", 0) + out += metric("hls_muxers_bytes_sent", "", 0) } } if !interfaceIsEmpty(m.rtspServer) { //nolint:dupl func() { res := m.rtspServer.apiConnsList() - if res.err == nil { + if res.err == nil && len(res.data.Items) != 0 { for id, i := range res.data.Items { tags := "{id=\"" + id + "\"}" - out += metric("rtsp_conns"+tags, 1) - out += metric("rtsp_conns_bytes_received"+tags, int64(i.BytesReceived)) - out += metric("rtsp_conns_bytes_sent"+tags, int64(i.BytesSent)) + out += metric("rtsp_conns", tags, 1) + out += metric("rtsp_conns_bytes_received", tags, int64(i.BytesReceived)) + out += metric("rtsp_conns_bytes_sent", tags, int64(i.BytesSent)) } + } else { + out += metric("rtsp_conns", "", 0) + out += metric("rtsp_conns_bytes_received", "", 0) + out += metric("rtsp_conns_bytes_sent", "", 0) } }() func() { res := m.rtspServer.apiSessionsList() - if res.err == nil { + if res.err == nil && len(res.data.Items) != 0 { for id, i := range res.data.Items { tags := "{id=\"" + id + "\",state=\"" + i.State + "\"}" - out += metric("rtsp_sessions"+tags, 1) - out += metric("rtsp_sessions_bytes_received"+tags, int64(i.BytesReceived)) - out += metric("rtsp_sessions_bytes_sent"+tags, int64(i.BytesSent)) + out += metric("rtsp_sessions", tags, 1) + out += metric("rtsp_sessions_bytes_received", tags, int64(i.BytesReceived)) + out += metric("rtsp_sessions_bytes_sent", tags, int64(i.BytesSent)) } + } else { + out += metric("rtsp_sessions", "", 0) + out += metric("rtsp_sessions_bytes_received", "", 0) + out += metric("rtsp_sessions_bytes_sent", "", 0) } }() } @@ -142,50 +155,66 @@ func (m *metrics) onMetrics(ctx *gin.Context) { if !interfaceIsEmpty(m.rtspsServer) { //nolint:dupl func() { res := m.rtspsServer.apiConnsList() - if res.err == nil { + if res.err == nil && len(res.data.Items) != 0 { for id, i := range res.data.Items { tags := "{id=\"" + id + "\"}" - out += metric("rtsps_conns"+tags, 1) - out += metric("rtsps_conns_bytes_received"+tags, int64(i.BytesReceived)) - out += metric("rtsps_conns_bytes_sent"+tags, int64(i.BytesSent)) + out += metric("rtsps_conns", tags, 1) + out += metric("rtsps_conns_bytes_received", tags, int64(i.BytesReceived)) + out += metric("rtsps_conns_bytes_sent", tags, int64(i.BytesSent)) } + } else { + out += metric("rtsps_conns", "", 0) + out += metric("rtsps_conns_bytes_received", "", 0) + out += metric("rtsps_conns_bytes_sent", "", 0) } }() func() { res := m.rtspsServer.apiSessionsList() - if res.err == nil { + if res.err == nil && len(res.data.Items) != 0 { for id, i := range res.data.Items { tags := "{id=\"" + id + "\",state=\"" + i.State + "\"}" - out += metric("rtsps_sessions"+tags, 1) - out += metric("rtsps_sessions_bytes_received"+tags, int64(i.BytesReceived)) - out += metric("rtsps_sessions_bytes_sent"+tags, int64(i.BytesSent)) + out += metric("rtsps_sessions", tags, 1) + out += metric("rtsps_sessions_bytes_received", tags, int64(i.BytesReceived)) + out += metric("rtsps_sessions_bytes_sent", tags, int64(i.BytesSent)) } + } else { + out += metric("rtsps_sessions", "", 0) + out += metric("rtsps_sessions_bytes_received", "", 0) + out += metric("rtsps_sessions_bytes_sent", "", 0) } }() } if !interfaceIsEmpty(m.rtmpServer) { res := m.rtmpServer.apiConnsList() - if res.err == nil { + if res.err == nil && len(res.data.Items) != 0 { for id, i := range res.data.Items { tags := "{id=\"" + id + "\",state=\"" + i.State + "\"}" - out += metric("rtmp_conns"+tags, 1) - out += metric("rtmp_conns_bytes_received"+tags, int64(i.BytesReceived)) - out += metric("rtmp_conns_bytes_sent"+tags, int64(i.BytesSent)) + out += metric("rtmp_conns", tags, 1) + out += metric("rtmp_conns_bytes_received", tags, int64(i.BytesReceived)) + out += metric("rtmp_conns_bytes_sent", tags, int64(i.BytesSent)) } + } else { + out += metric("rtmp_conns", "", 0) + out += metric("rtmp_conns_bytes_received", "", 0) + out += metric("rtmp_conns_bytes_sent", "", 0) } } if !interfaceIsEmpty(m.webRTCServer) { res := m.webRTCServer.apiConnsList() - if res.err == nil { + if res.err == nil && len(res.data.Items) != 0 { for id, i := range res.data.Items { tags := "{id=\"" + id + "\"}" - out += metric("webrtc_conns"+tags, 1) - out += metric("webrtc_conns_bytes_received"+tags, int64(i.BytesReceived)) - out += metric("webrtc_conns_bytes_sent"+tags, int64(i.BytesSent)) + out += metric("webrtc_conns", tags, 1) + out += metric("webrtc_conns_bytes_received", tags, int64(i.BytesReceived)) + out += metric("webrtc_conns_bytes_sent", tags, int64(i.BytesSent)) } + } else { + out += metric("webrtc_conns", "", 0) + out += metric("webrtc_conns_bytes_received", "", 0) + out += metric("webrtc_conns_bytes_sent", "", 0) } } diff --git a/internal/core/metrics_test.go b/internal/core/metrics_test.go index 992d5ddb..ea315a44 100644 --- a/internal/core/metrics_test.go +++ b/internal/core/metrics_test.go @@ -2,6 +2,7 @@ package core import ( "crypto/tls" + "fmt" "io" "net" "net/http" @@ -17,6 +18,25 @@ import ( "github.com/aler9/mediamtx/internal/rtmp" ) +func pullMetrics() ([]byte, error) { + req, err := http.NewRequest(http.MethodGet, "http://localhost:9998/metrics", nil) + if err != nil { + return nil, err + } + + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return nil, fmt.Errorf("bad status code: %v", res.StatusCode) + } + + return io.ReadAll(res.Body) +} + func TestMetrics(t *testing.T) { serverCertFpath, err := writeTempFile(serverCert) require.NoError(t, err) @@ -37,6 +57,32 @@ func TestMetrics(t *testing.T) { require.Equal(t, true, ok) defer p.Close() + bo, err := pullMetrics() + require.NoError(t, err) + + require.Equal(t, `paths 0 +hls_muxers 0 +hls_muxers_bytes_sent 0 +rtsp_conns 0 +rtsp_conns_bytes_received 0 +rtsp_conns_bytes_sent 0 +rtsp_sessions 0 +rtsp_sessions_bytes_received 0 +rtsp_sessions_bytes_sent 0 +rtsps_conns 0 +rtsps_conns_bytes_received 0 +rtsps_conns_bytes_sent 0 +rtsps_sessions 0 +rtsps_sessions_bytes_received 0 +rtsps_sessions_bytes_sent 0 +rtmp_conns 0 +rtmp_conns_bytes_received 0 +rtmp_conns_bytes_sent 0 +webrtc_conns 0 +webrtc_conns_bytes_received 0 +webrtc_conns_bytes_sent 0 +`, string(bo)) + medi := testMediaH264 source := gortsplib.Client{} @@ -83,15 +129,7 @@ func TestMetrics(t *testing.T) { require.Equal(t, 200, res.StatusCode) }() - req, err := http.NewRequest(http.MethodGet, "http://localhost:9998/metrics", nil) - require.NoError(t, err) - - res, err := http.DefaultClient.Do(req) - require.NoError(t, err) - defer res.Body.Close() - require.Equal(t, http.StatusOK, res.StatusCode) - - bo, err := io.ReadAll(res.Body) + bo, err = pullMetrics() require.NoError(t, err) require.Regexp(t, @@ -118,6 +156,9 @@ func TestMetrics(t *testing.T) { `rtmp_conns\{id=".*?",state="publish"\} 1`+"\n"+ `rtmp_conns_bytes_received\{id=".*?",state="publish"\} [0-9]+`+"\n"+ `rtmp_conns_bytes_sent\{id=".*?",state="publish"\} [0-9]+`+"\n"+ + `webrtc_conns 0`+"\n"+ + `webrtc_conns_bytes_received 0`+"\n"+ + `webrtc_conns_bytes_sent 0`+"\n"+ "$", string(bo)) }