diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f847a0 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Oven-Exporter +An Prometheus Exporter for OvenMediaEngine + +(it provides also a small API-Client for OvenMediaEngine) +Be welcome to improve it. diff --git a/config_example.toml b/config_example.toml index 70e8a02..36620ae 100644 --- a/config_example.toml +++ b/config_example.toml @@ -1,3 +1,6 @@ +listen = ":8080" + +[api] host = "http://1.2.3.4:8081" token = "ome-access-token" diff --git a/fetch.go b/fetch.go deleted file mode 100644 index 0ba699c..0000000 --- a/fetch.go +++ /dev/null @@ -1,61 +0,0 @@ -package main - -import ( - "dev.sum7.eu/genofire/oven-exporter/api" - "github.com/bdlm/log" -) - -func statsLog(resp *api.ResponseStats, log *log.Entry) { - logger := log - if resp.Data != nil { - logger = logger.WithFields(map[string]interface{}{ - "max_clients": resp.Data.MaxTotalConnections, - "clients": resp.Data.TotalConnections, - }) - } - logger.Info(resp.Message) -} - -func fetch(client *api.Client) { - respList, err := client.RequestListVHosts() - if err != nil { - log.Panicf("unable to fetch vhosts: %s", err) - } - for _, vhost := range respList.Data { - logVhost := log.WithField("vhost", vhost) - resp, err := client.RequestStatsVHost(vhost) - if err != nil { - logVhost.Errorf("error on request: %s", err) - } else { - statsLog(resp, logVhost) - } - respList, err = client.RequestListApps(vhost) - if err != nil { - logVhost.Errorf("unable to fetch apps: %s", err) - continue - } - for _, app := range respList.Data { - logApp := logVhost.WithField("app", app) - resp, err = client.RequestStatsApp(vhost, app) - if err != nil { - logApp.Errorf("error on request: %s", err) - } else { - statsLog(resp, logApp) - } - respList, err = client.RequestListStreams(vhost, app) - if err != nil { - logApp.Errorf("unable to fetch stream: %s", err) - continue - } - for _, stream := range respList.Data { - logStream := logApp.WithField("stream", stream) - req, err := client.RequestStatsStream(vhost, app, stream) - if err != nil { - logStream.Errorf("error on request: %s", err) - continue - } - statsLog(req, logStream) - } - } - } -} diff --git a/go.mod b/go.mod index 321b197..b56984e 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,10 @@ module dev.sum7.eu/genofire/oven-exporter go 1.16 require ( - dev.sum7.eu/genofire/golang-lib v0.0.0-20210719163544-fb766ca32b7c // indirect + dev.sum7.eu/genofire/golang-lib v0.0.0-20210719163544-fb766ca32b7c github.com/bdlm/log v0.1.20 github.com/bdlm/std v1.0.1 // indirect + github.com/prometheus/client_golang v1.10.0 github.com/stretchr/testify v1.7.0 // indirect golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect diff --git a/go.sum b/go.sum index c0e15f0..8f73df5 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,7 @@ github.com/bdlm/std v1.0.1 h1:USdxays+0tgB3BJCEQ9z942tmTWmzpVPC7jCvczsj/I= github.com/bdlm/std v1.0.1/go.mod h1:dittT3gnvbHQ4P+1UbkdSwkHFHVl1gx8qYu4zIFyB+Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw= @@ -39,6 +40,7 @@ github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414 github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenjiandongx/ginprom v0.0.0-20201217063207-fe11b7f55a35/go.mod h1:lINNCb1ZH3c0uL/9ApaQ8muR4QILsi0STj8Ojt8ZmwU= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= @@ -124,6 +126,7 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= @@ -273,6 +276,7 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -333,23 +337,27 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg= github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.18.0 h1:WCVKW7aL6LEe1uryfI9dnEc2ZqNB1Fn0ok930v0iL1Y= github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= @@ -548,6 +556,7 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= diff --git a/main.go b/main.go index c593ddc..4c8eabe 100644 --- a/main.go +++ b/main.go @@ -2,15 +2,19 @@ package main import ( "flag" + "net/http" "dev.sum7.eu/genofire/golang-lib/file" "github.com/bdlm/log" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" "dev.sum7.eu/genofire/oven-exporter/api" ) type configData struct { - API *api.Client `toml:"api"` + API *api.Client `toml:"api"` + Listen string `toml:"listen"` } func main() { @@ -25,5 +29,8 @@ func main() { log.Panicf("open config file: %s", err) } config.API.SetToken(config.API.Token) - fetch(config.API) + + prometheus.MustRegister(config) + http.Handle("/metrics", promhttp.Handler()) + log.Fatal(http.ListenAndServe(config.Listen, nil)) } diff --git a/prometheus.go b/prometheus.go new file mode 100644 index 0000000..4515a86 --- /dev/null +++ b/prometheus.go @@ -0,0 +1,114 @@ +package main + +import ( + "dev.sum7.eu/genofire/oven-exporter/api" + "github.com/bdlm/log" + "github.com/prometheus/client_golang/prometheus" +) + +var ( + promDescStatsVHostClientTotal = prometheus.NewDesc("oven_vhost_client_total", "client count of current vhost", []string{"vhost"}, prometheus.Labels{}) + promDescStatsVHostClientMax = prometheus.NewDesc("oven_vhost_client_max", "client max of current vhost", []string{"vhost"}, prometheus.Labels{}) + promDescStatsVHostBytesInTotal = prometheus.NewDesc("oven_vhost_bytes_in_total", "total bytes in vhost", []string{"vhost"}, prometheus.Labels{}) + promDescStatsVHostBytesOutTotal = prometheus.NewDesc("oven_vhost_bytes_out_total", "total bytes out vhost", []string{"vhost"}, prometheus.Labels{}) + promDescStatsVHost = []*prometheus.Desc{ + promDescStatsVHostClientTotal, + promDescStatsVHostClientMax, + promDescStatsVHostBytesInTotal, + promDescStatsVHostBytesOutTotal, + } + + promDescStatsAppClientTotal = prometheus.NewDesc("oven_app_client_total", "client count of current app", []string{"vhost", "app"}, prometheus.Labels{}) + promDescStatsAppClientMax = prometheus.NewDesc("oven_app_client_max", "client max of current app", []string{"vhost", "app"}, prometheus.Labels{}) + promDescStatsAppBytesInTotal = prometheus.NewDesc("oven_app_bytes_in_total", "total bytes in app", []string{"vhost", "app"}, prometheus.Labels{}) + promDescStatsAppBytesOutTotal = prometheus.NewDesc("oven_app_bytes_out_total", "total bytes out app", []string{"vhost", "app"}, prometheus.Labels{}) + promDescStatsApp = []*prometheus.Desc{ + promDescStatsAppClientTotal, + promDescStatsAppClientMax, + promDescStatsAppBytesInTotal, + promDescStatsAppBytesOutTotal, + } + + promDescStatsStreamClientTotal = prometheus.NewDesc("oven_stream_client_total", "client count of current stream", []string{"vhost", "app", "stream"}, prometheus.Labels{}) + promDescStatsStreamClientMax = prometheus.NewDesc("oven_stream_client_max", "client max of current stream", []string{"vhost", "app", "stream"}, prometheus.Labels{}) + promDescStatsStreamBytesInTotal = prometheus.NewDesc("oven_stream_bytes_in_total", "total bytes in stream", []string{"vhost", "app", "stream"}, prometheus.Labels{}) + promDescStatsStreamBytesOutTotal = prometheus.NewDesc("oven_stream_bytes_out_total", "total bytes out stream", []string{"vhost", "app", "stream"}, prometheus.Labels{}) + promDescStatsStream = []*prometheus.Desc{ + promDescStatsStreamClientTotal, + promDescStatsStreamClientMax, + promDescStatsStreamBytesInTotal, + promDescStatsStreamBytesOutTotal, + } +) + +func ResponseStatsToMetrics(resp *api.ResponseStats, descs []*prometheus.Desc, labels ...string) []prometheus.Metric { + if resp == nil || resp.Data == nil { + return nil + } + list := []prometheus.Metric{} + if m, err := prometheus.NewConstMetric(descs[0], prometheus.GaugeValue, float64(resp.Data.TotalConnections), labels...); err == nil { + list = append(list, m) + } + if m, err := prometheus.NewConstMetric(descs[1], prometheus.GaugeValue, float64(resp.Data.MaxTotalConnections), labels...); err == nil { + list = append(list, m) + } + if m, err := prometheus.NewConstMetric(descs[2], prometheus.GaugeValue, float64(resp.Data.TotalBytesIn), labels...); err == nil { + list = append(list, m) + } + if m, err := prometheus.NewConstMetric(descs[3], prometheus.GaugeValue, float64(resp.Data.TotalBytesOut), labels...); err == nil { + list = append(list, m) + } + return list +} + +func (c *configData) Describe(d chan<- *prometheus.Desc) { + for _, desc := range promDescStatsVHost { + d <- desc + } + for _, desc := range promDescStatsApp { + d <- desc + } + for _, desc := range promDescStatsStream { + d <- desc + } +} + +func (c *configData) Collect(metrics chan<- prometheus.Metric) { + respList, err := c.API.RequestListVHosts() + if err != nil { + log.Panicf("unable to fetch vhosts: %s", err) + } + for _, vhost := range respList.Data { + logVhost := log.WithField("vhost", vhost) + if resp, err := c.API.RequestStatsVHost(vhost); err == nil { + for _, m := range ResponseStatsToMetrics(resp, promDescStatsVHost, vhost) { + metrics <- m + } + } + respList, err = c.API.RequestListApps(vhost) + if err != nil { + logVhost.Errorf("unable to fetch apps: %s", err) + continue + } + for _, app := range respList.Data { + logApp := logVhost.WithField("app", app) + if resp, err := c.API.RequestStatsApp(vhost, app); err == nil { + for _, m := range ResponseStatsToMetrics(resp, promDescStatsApp, vhost, app) { + metrics <- m + } + } + respList, err = c.API.RequestListStreams(vhost, app) + if err != nil { + logApp.Errorf("unable to fetch stream: %s", err) + continue + } + for _, stream := range respList.Data { + if resp, err := c.API.RequestStatsStream(vhost, app, stream); err == nil { + for _, m := range ResponseStatsToMetrics(resp, promDescStatsStream, vhost, app, stream) { + metrics <- m + } + } + } + } + } +}