diff --git a/README.md b/README.md index e49c989..65fcd8c 100644 --- a/README.md +++ b/README.md @@ -21,22 +21,27 @@ func main() { ``` +## with sqlite + +```shell +go build -tags sqlite -o monibuca_sqlite +./monibuca_sqlite -c config.yaml +``` ## More Example see example directory +# Prometheus + +```yaml +scrape_configs: + - job_name: "monibuca" + metrics_path: "/api/metrics" + static_configs: + - targets: ["localhost:8080"] +``` + # Create Plugin -```go - -import ( - "m7s.live/m7s/v5" -) - -type MyPlugin struct { - m7s.Plugin -} - -var _ = m7s.InstallPlugin[MyPlugin]() -``` \ No newline at end of file +see [plugin](./plugin/README.md) \ No newline at end of file diff --git a/go.mod b/go.mod index b391a20..7a5e50d 100644 --- a/go.mod +++ b/go.mod @@ -42,6 +42,7 @@ require ( github.com/VictoriaMetrics/fastcache v1.12.2 // indirect github.com/VictoriaMetrics/metrics v1.35.1 // indirect github.com/VictoriaMetrics/metricsql v0.76.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect github.com/c0deltin/duckdb-driver v0.1.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732 // indirect @@ -66,6 +67,7 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/ncruces/julianday v1.0.0 // indirect github.com/pion/datachannel v1.5.6 // indirect @@ -80,6 +82,9 @@ require ( github.com/pion/turn/v2 v2.1.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/samber/lo v1.44.0 // indirect @@ -119,6 +124,7 @@ require ( github.com/gorilla/websocket v1.5.1 github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/phsym/console-slog v0.3.1 + github.com/prometheus/client_golang v1.20.4 github.com/shirou/gopsutil/v3 v3.24.3 go.uber.org/mock v0.4.0 // indirect golang.org/x/crypto v0.26.0 // indirect diff --git a/go.sum b/go.sum index 342cf9d..a428ed7 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,8 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/asavie/xdp v0.3.3 h1:b5Aa3EkMJYBeUO5TxPTIAa4wyUqYcsQr2s8f6YLJXhE= github.com/asavie/xdp v0.3.3/go.mod h1:Vv5p+3mZiDh7ImdSvdon3E78wXyre7df5V58ATdIYAY= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bluenviron/mediacommon v1.9.2 h1:EHcvoC5YMXRcFE010bTNf07ZiSlB/e/AdZyG7GsEYN0= github.com/bluenviron/mediacommon v1.9.2/go.mod h1:lt8V+wMyPw8C69HAqDWV5tsAwzN9u2Z+ca8B6C//+n0= github.com/c0deltin/duckdb-driver v0.1.0 h1:g/RAwwNDFd2HmrnqF0oPE0aY7W6F6uFJbf1+zs285eM= @@ -152,6 +154,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/ncruces/go-sqlite3 v0.18.1 h1:iN8IMZV5EMxpH88NUac9vId23eTKNFUhP7jgY0EBbNc= github.com/ncruces/go-sqlite3 v0.18.1/go.mod h1:eEOyZnW1dGTJ+zDpMuzfYamEUBtdFz5zeYhqLBtHxvM= github.com/ncruces/go-sqlite3/gormlite v0.18.0 h1:KqP9a9wlX/Ba+yG+aeVX4pnNBNdaSO6xHdNDWzPxPnk= @@ -237,6 +241,14 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= +github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/quic-go/quic-go v0.43.1 h1:fLiMNfQVe9q2JvSsiXo4fXOEguXHGGl9+6gLp4RPeZQ= github.com/quic-go/quic-go v0.43.1/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= diff --git a/pkg/track.go b/pkg/track.go index f662951..9c3da0e 100644 --- a/pkg/track.go +++ b/pkg/track.go @@ -3,11 +3,12 @@ package pkg import ( "context" "log/slog" + "reflect" + "time" + "m7s.live/m7s/v5/pkg/codec" "m7s.live/m7s/v5/pkg/config" "m7s.live/m7s/v5/pkg/task" - "reflect" - "time" "m7s.live/m7s/v5/pkg/util" ) diff --git a/server.go b/server.go index efc664b..bc3f0c1 100644 --- a/server.go +++ b/server.go @@ -21,6 +21,8 @@ import ( "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" myip "github.com/husanpao/ip" "github.com/phsym/console-slog" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "gopkg.in/yaml.v3" @@ -80,6 +82,22 @@ type ( lastSummaryTime time.Time lastSummary *pb.SummaryResponse conf any + prometheusDesc prometheusDesc + } + prometheusDesc struct { + CPU struct { + UserTime, Usage, SystemTime, IdleTime *prometheus.Desc + } + Memory struct { + Total, Used, UsedPercent, Free *prometheus.Desc + } + Disk struct { + Total, Used, UsedPercent, Free *prometheus.Desc + } + Net struct { + BytesSent, BytesRecv, PacketsSent, PacketsRecv, ErrsSent, ErrsRecv, DroppedSent, DroppedRecv *prometheus.Desc + } + BPS, FPS *prometheus.Desc } CheckSubWaitTimeout struct { task.TickTask @@ -93,6 +111,31 @@ type ( RawConfig = map[string]map[string]any ) +func (d *prometheusDesc) init() { + d.CPU.UserTime = prometheus.NewDesc("cpu_user_time", "CPU user time", nil, nil) + d.CPU.Usage = prometheus.NewDesc("cpu_usage", "CPU usage", nil, nil) + d.CPU.SystemTime = prometheus.NewDesc("cpu_system_time", "CPU system time", nil, nil) + d.CPU.IdleTime = prometheus.NewDesc("cpu_idle_time", "CPU idle time", nil, nil) + d.Memory.Total = prometheus.NewDesc("memory_total", "Memory total", nil, nil) + d.Memory.Used = prometheus.NewDesc("memory_used", "Memory used", nil, nil) + d.Memory.UsedPercent = prometheus.NewDesc("memory_used_percent", "Memory used percent", nil, nil) + d.Memory.Free = prometheus.NewDesc("memory_free", "Memory free", nil, nil) + d.Disk.Total = prometheus.NewDesc("disk_total", "Disk total", nil, nil) + d.Disk.Used = prometheus.NewDesc("disk_used", "Disk used", nil, nil) + d.Disk.UsedPercent = prometheus.NewDesc("disk_used_percent", "Disk used percent", nil, nil) + d.Disk.Free = prometheus.NewDesc("disk_free", "Disk free", nil, nil) + d.Net.BytesSent = prometheus.NewDesc("net_bytes_sent", "Network bytes sent", nil, nil) + d.Net.BytesRecv = prometheus.NewDesc("net_bytes_recv", "Network bytes received", nil, nil) + d.Net.PacketsSent = prometheus.NewDesc("net_packets_sent", "Network packets sent", nil, nil) + d.Net.PacketsRecv = prometheus.NewDesc("net_packets_recv", "Network packets received", nil, nil) + d.Net.ErrsSent = prometheus.NewDesc("net_errs_sent", "Network errors sent", nil, nil) + d.Net.ErrsRecv = prometheus.NewDesc("net_errs_recv", "Network errors received", nil, nil) + d.Net.DroppedSent = prometheus.NewDesc("net_dropped_sent", "Network dropped sent", nil, nil) + d.Net.DroppedRecv = prometheus.NewDesc("net_dropped_recv", "Network dropped received", nil, nil) + d.BPS = prometheus.NewDesc("bps", "Bytes Per Second", []string{"streamPath", "pluginName", "trackType"}, nil) + d.FPS = prometheus.NewDesc("fps", "Frames Per Second", []string{"streamPath", "pluginName", "trackType"}, nil) +} + func (w *WaitStream) GetKey() string { return w.StreamPath } @@ -110,6 +153,7 @@ func NewServer(conf any) (s *Server) { "arch": sysruntime.GOARCH, "cpus": int32(sysruntime.NumCPU()), } + s.prometheusDesc.init() return } @@ -147,10 +191,16 @@ func (s *Server) GetKey() uint32 { return s.ID } +type errLogger struct { +} + +func (l errLogger) Println(v ...interface{}) { + slog.Error("Exporter promhttp err: ", v...) +} + func (s *Server) Start() (err error) { s.Server = s s.handler = s - //s.config.HTTP.ListenAddrTLS = ":8443" httpConf, tcpConf := &s.config.HTTP, &s.config.TCP httpConf.ListenAddr = ":8080" tcpConf.ListenAddr = ":50051" @@ -192,6 +242,7 @@ func (s *Server) Start() (err error) { s.Error("SetCrashOutput", "error", err) return } + s.registerHandler(map[string]http.HandlerFunc{ "/api/config/json/{name}": s.api_Config_JSON_, "/api/stream/annexb/{streamPath...}": s.api_Stream_AnnexB_, @@ -239,9 +290,25 @@ func (s *Server) Start() (err error) { s.AddTaskLazy(&s.Pushs) s.AddTaskLazy(&s.Transforms) s.AddTaskLazy(&s.Devices) + promReg := prometheus.NewPedanticRegistry() + promReg.MustRegister(s) for _, plugin := range plugins { - plugin.Init(s, cg[strings.ToLower(plugin.Name)]) + p := plugin.Init(s, cg[strings.ToLower(plugin.Name)]) + if !p.Disabled { + if collector, ok := p.handler.(prometheus.Collector); ok { + promReg.MustRegister(collector) + } + } } + promhttpHandler := promhttp.HandlerFor(prometheus.Gatherers{ + prometheus.DefaultGatherer, + promReg, + }, + promhttp.HandlerOpts{ + ErrorLog: errLogger{}, + ErrorHandling: promhttp.ContinueOnError, + }) + s.handle("/api/metrics", promhttpHandler) if grpcServer != nil { s.AddTask(grpcServer, s.Logger) } @@ -332,3 +399,20 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { _, _ = fmt.Fprintf(w, "%s\n", api) } } + +func (s *Server) Describe(ch chan<- *prometheus.Desc) { + ch <- s.prometheusDesc.BPS + ch <- s.prometheusDesc.FPS +} + +func (s *Server) Collect(ch chan<- prometheus.Metric) { + s.Call(func() error { + for stream := range s.Streams.Range { + ch <- prometheus.MustNewConstMetric(s.prometheusDesc.BPS, prometheus.GaugeValue, float64(stream.VideoTrack.AVTrack.BPS), stream.StreamPath, stream.Plugin.Meta.Name, "video") + ch <- prometheus.MustNewConstMetric(s.prometheusDesc.FPS, prometheus.GaugeValue, float64(stream.VideoTrack.AVTrack.FPS), stream.StreamPath, stream.Plugin.Meta.Name, "video") + ch <- prometheus.MustNewConstMetric(s.prometheusDesc.BPS, prometheus.GaugeValue, float64(stream.AudioTrack.AVTrack.BPS), stream.StreamPath, stream.Plugin.Meta.Name, "audio") + ch <- prometheus.MustNewConstMetric(s.prometheusDesc.FPS, prometheus.GaugeValue, float64(stream.AudioTrack.AVTrack.FPS), stream.StreamPath, stream.Plugin.Meta.Name, "audio") + } + return nil + }) +}