diff --git a/README.md b/README.md index 27ca8e41..7409e664 100644 --- a/README.md +++ b/README.md @@ -430,6 +430,7 @@ rtsps_sessions{state="publish"} 0 rtmp_conns{state="idle"} 0 rtmp_conns{state="read"} 0 rtmp_conns{state="publish"} 1 +hls_muxers{name=""} 1 ``` where: @@ -444,6 +445,7 @@ where: * `rtmp_conns{state="idle"}` is the count of RTMP connections that are idle * `rtmp_conns{state="read"}` is the count of RTMP connections that are reading * `rtmp_conns{state="publish"}` is the count of RTMP connections that are publishing +* `hls_muxers{name=""}` is replicated for every HLS muxer and shows the name and state of every HLS muxer ### pprof diff --git a/internal/core/core.go b/internal/core/core.go index 81b2702d..5eedc629 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -328,6 +328,7 @@ func (p *Core) createResources(initial bool) error { p.conf.HLSAllowOrigin, p.conf.ReadBufferCount, p.pathManager, + p.metrics, p) if err != nil { return err @@ -458,7 +459,8 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) { newConf.HLSSegmentDuration != p.conf.HLSSegmentDuration || newConf.HLSAllowOrigin != p.conf.HLSAllowOrigin || newConf.ReadBufferCount != p.conf.ReadBufferCount || - closePathManager { + closePathManager || + closeMetrics { closeHLSServer = true } diff --git a/internal/core/hls_server.go b/internal/core/hls_server.go index d8a6f066..02713b19 100644 --- a/internal/core/hls_server.go +++ b/internal/core/hls_server.go @@ -28,6 +28,7 @@ type hlsServer struct { hlsAllowOrigin string readBufferCount int pathManager *pathManager + metrics *metrics parent hlsServerParent ctx context.Context @@ -52,6 +53,7 @@ func newHLSServer( hlsAllowOrigin string, readBufferCount int, pathManager *pathManager, + metrics *metrics, parent hlsServerParent, ) (*hlsServer, error) { ln, err := net.Listen("tcp", address) @@ -69,6 +71,7 @@ func newHLSServer( readBufferCount: readBufferCount, pathManager: pathManager, parent: parent, + metrics: metrics, ctx: ctx, ctxCancel: ctxCancel, ln: ln, @@ -83,6 +86,10 @@ func newHLSServer( s.pathManager.onHLSServerSet(s) + if s.metrics != nil { + s.metrics.onHLSServerSet(s) + } + s.wg.Add(1) go s.run() @@ -148,6 +155,10 @@ outer: hs.Shutdown(context.Background()) s.pathManager.onHLSServerSet(nil) + + if s.metrics != nil { + s.metrics.onHLSServerSet(nil) + } } func (s *hlsServer) onRequest(ctx *gin.Context) { diff --git a/internal/core/metrics.go b/internal/core/metrics.go index 262ea543..41d002d8 100644 --- a/internal/core/metrics.go +++ b/internal/core/metrics.go @@ -13,7 +13,7 @@ import ( "github.com/aler9/rtsp-simple-server/internal/logger" ) -func formatMetric(key string, value int64) string { +func metric(key string, value int64) string { return key + " " + strconv.FormatInt(value, 10) + "\n" } @@ -29,6 +29,10 @@ type metricsRTMPServer interface { onAPIRTMPConnsList(req apiRTMPConnsListReq) apiRTMPConnsListRes } +type metricsHLSServer interface { + onAPIHLSMuxersList(req apiHLSMuxersListReq) apiHLSMuxersListRes +} + type metricsParent interface { Log(logger.Level, string, ...interface{}) } @@ -42,6 +46,7 @@ type metrics struct { rtspServer metricsRTSPServer rtspsServer metricsRTSPServer rtmpServer metricsRTMPServer + hlsServer metricsHLSServer } func newMetrics( @@ -87,9 +92,9 @@ func (m *metrics) onMetrics(ctx *gin.Context) { if res.Err == nil { for name, p := range res.Data.Items { if p.SourceReady { - out += formatMetric("paths{name=\""+name+"\",state=\"ready\"}", 1) + out += metric("paths{name=\""+name+"\",state=\"ready\"}", 1) } else { - out += formatMetric("paths{name=\""+name+"\",state=\"notReady\"}", 1) + out += metric("paths{name=\""+name+"\",state=\"notReady\"}", 1) } } } @@ -112,11 +117,11 @@ func (m *metrics) onMetrics(ctx *gin.Context) { } } - out += formatMetric("rtsp_sessions{state=\"idle\"}", + out += metric("rtsp_sessions{state=\"idle\"}", idleCount) - out += formatMetric("rtsp_sessions{state=\"read\"}", + out += metric("rtsp_sessions{state=\"read\"}", readCount) - out += formatMetric("rtsp_sessions{state=\"publish\"}", + out += metric("rtsp_sessions{state=\"publish\"}", publishCount) } } @@ -139,11 +144,11 @@ func (m *metrics) onMetrics(ctx *gin.Context) { } } - out += formatMetric("rtsps_sessions{state=\"idle\"}", + out += metric("rtsps_sessions{state=\"idle\"}", idleCount) - out += formatMetric("rtsps_sessions{state=\"read\"}", + out += metric("rtsps_sessions{state=\"read\"}", readCount) - out += formatMetric("rtsps_sessions{state=\"publish\"}", + out += metric("rtsps_sessions{state=\"publish\"}", publishCount) } } @@ -166,15 +171,24 @@ func (m *metrics) onMetrics(ctx *gin.Context) { } } - out += formatMetric("rtmp_conns{state=\"idle\"}", + out += metric("rtmp_conns{state=\"idle\"}", idleCount) - out += formatMetric("rtmp_conns{state=\"read\"}", + out += metric("rtmp_conns{state=\"read\"}", readCount) - out += formatMetric("rtmp_conns{state=\"publish\"}", + out += metric("rtmp_conns{state=\"publish\"}", publishCount) } } + if !interfaceIsEmpty(m.hlsServer) { + res := m.hlsServer.onAPIHLSMuxersList(apiHLSMuxersListReq{}) + if res.Err == nil { + for name := range res.Data.Items { + out += metric("hls_muxers{name=\""+name+"\"}", 1) + } + } + } + ctx.Writer.WriteHeader(http.StatusOK) io.WriteString(ctx.Writer, out) } @@ -206,3 +220,10 @@ func (m *metrics) onRTMPServerSet(s metricsRTMPServer) { defer m.mutex.Unlock() m.rtmpServer = s } + +// onHLSServerSet is called by hlsServer. +func (m *metrics) onHLSServerSet(s metricsHLSServer) { + m.mutex.Lock() + defer m.mutex.Unlock() + m.hlsServer = s +} diff --git a/internal/core/metrics_test.go b/internal/core/metrics_test.go index 33575c0f..d4ae5e6a 100644 --- a/internal/core/metrics_test.go +++ b/internal/core/metrics_test.go @@ -47,6 +47,13 @@ func TestMetrics(t *testing.T) { require.NoError(t, err) defer cnt1.close() + func() { + res, err := http.Get("http://localhost:8888/rtsp_path/index.m3u8") + require.NoError(t, err) + defer res.Body.Close() + require.Equal(t, 200, res.StatusCode) + }() + req, err := http.NewRequest(http.MethodGet, "http://localhost:9998/metrics", nil) require.NoError(t, err) @@ -66,6 +73,7 @@ func TestMetrics(t *testing.T) { } require.Equal(t, map[string]string{ + "hls_muxers{name=\"rtsp_path\"}": "1", "paths{name=\"rtsp_path\",state=\"ready\"}": "1", "paths{name=\"rtmp_path\",state=\"ready\"}": "1", "rtmp_conns{state=\"idle\"}": "0",