timeout context for fetching jitsi stats and a fetch error metric
This commit is contained in:
parent
5e5f992967
commit
ea3684a182
24
README.md
24
README.md
@ -3,4 +3,26 @@
|
||||
[](https://cloud.drone.io/xsteadfastx/jitsiexporter)
|
||||
[](https://quay.io/repository/xsteadfastx/jitsiexporter)
|
||||
|
||||
a jitsi meet prometheus exporter
|
||||
A Jitsi meet prometheus exporter.
|
||||
|
||||
Usage of ./jitsiexporter_linux_amd64:
|
||||
-debug
|
||||
Enable debug.
|
||||
-host string
|
||||
Host to listen on. (default "localhost")
|
||||
-interval duration
|
||||
Seconds to wait before scraping. (default 30s)
|
||||
-port int
|
||||
Port to listen on. (default 9700)
|
||||
-url string
|
||||
URL of Jitsi Videobridge Colibri Stats.
|
||||
-version
|
||||
Prints version.
|
||||
|
||||
## Usage
|
||||
|
||||
For a docker based setup, you can use the docker image [quay.io/xsteadfastx/jitsiexporter](https://quay.io/repository/xsteadfastx/jitsiexporter).
|
||||
|
||||
1. [Enable](https://github.com/jitsi/jitsi-videobridge/blob/master/doc/statistics.md) `/colibri/stats` for the Jitsi videobridge. When you use the Jitsi docker setup use environment variable `JVB_ENABLE_APIS=rest,colibri`.
|
||||
2. Be sure that the exporter and the videobridge API can communicate. In the docker Jitsi setup: Add the `jitsiexporter` to the `jitsi-meet_meet.jitsi`-network. The url would be `http://jitsi-meet_jvb_1:8080`.
|
||||
3. A failed scrape metric is exporter as `jitsi_fetch_errors`.
|
||||
|
@ -15,7 +15,7 @@ func main() {
|
||||
ver := flag.Bool("version", false, "Prints version.")
|
||||
url := flag.String("url", "", "URL of Jitsi Videobridge Colibri Stats.")
|
||||
debug := flag.Bool("debug", false, "Enable debug.")
|
||||
interval := flag.Duration("interval", 10*time.Second, "Seconds to wait before scraping.") // nolint: gomnd
|
||||
interval := flag.Duration("interval", 30*time.Second, "Seconds to wait before scraping.") // nolint: gomnd
|
||||
port := flag.Int("port", 9700, "Port to listen on.")
|
||||
host := flag.String("host", "localhost", "Host to listen on.")
|
||||
flag.Parse()
|
||||
|
@ -1,6 +1,7 @@
|
||||
package jitsiexporter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@ -24,13 +25,28 @@ type Metrics struct {
|
||||
URL string
|
||||
Stater Stater
|
||||
mux sync.Mutex
|
||||
Errors prometheus.Counter
|
||||
Interval time.Duration
|
||||
}
|
||||
|
||||
func (m *Metrics) Update() error {
|
||||
m.mux.Lock()
|
||||
defer m.mux.Unlock()
|
||||
|
||||
now, err := m.Stater.Now(m.URL)
|
||||
|
||||
if err != nil {
|
||||
m.Errors.Inc()
|
||||
|
||||
for _, i := range m.Metrics {
|
||||
prometheus.Unregister(i.Gauge)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Metrics) Update() {
|
||||
now := m.Stater.Now(m.URL)
|
||||
log.Debug(now)
|
||||
|
||||
m.mux.Lock()
|
||||
for k, v := range now {
|
||||
fieldLogger := log.WithFields(log.Fields{"key": k})
|
||||
|
||||
@ -65,48 +81,99 @@ func (m *Metrics) Update() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
m.mux.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Resp *http.Response
|
||||
Error error
|
||||
}
|
||||
|
||||
func get(ctx context.Context, url string, resp chan Response) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
resp <- Response{Resp: nil, Error: err}
|
||||
return
|
||||
}
|
||||
|
||||
client := http.DefaultClient
|
||||
|
||||
res, err := client.Do(req.WithContext(ctx)) // nolint:bodyclose
|
||||
if err != nil {
|
||||
resp <- Response{Resp: nil, Error: err}
|
||||
}
|
||||
|
||||
resp <- Response{Resp: res, Error: nil}
|
||||
}
|
||||
|
||||
type Stater interface {
|
||||
Now(url string) map[string]interface{}
|
||||
Now(url string) (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
type colibri struct{}
|
||||
|
||||
func (c colibri) Now(url string) map[string]interface{} {
|
||||
func (c colibri) Now(url string) (map[string]interface{}, error) {
|
||||
s := make(map[string]interface{})
|
||||
resp, err := http.Get(url) // nolint:gosec
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) // nolint:gomnd
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer cancel()
|
||||
|
||||
res := make(chan Response)
|
||||
|
||||
var resp *http.Response
|
||||
|
||||
var err error
|
||||
|
||||
go get(ctx, url, res)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case r := <-res:
|
||||
err = r.Error
|
||||
resp = r.Resp
|
||||
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.NewDecoder(resp.Body).Decode(&s)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func collect(m *Metrics) {
|
||||
for {
|
||||
m.Update()
|
||||
time.Sleep(30 * time.Second) // nolint:gomnd
|
||||
err := m.Update()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
time.Sleep(m.Interval) // nolint:gomnd
|
||||
}
|
||||
}
|
||||
|
||||
func Serve(url string, debug bool, interval time.Duration, port int, host string) {
|
||||
s := colibri{}
|
||||
e := prometheus.NewCounter(prometheus.CounterOpts{Name: "jitsi_fetch_errors"})
|
||||
metrics := &Metrics{
|
||||
URL: url,
|
||||
Stater: s,
|
||||
Metrics: make(map[string]Metric),
|
||||
Errors: e,
|
||||
Interval: interval,
|
||||
}
|
||||
|
||||
prometheus.MustRegister(e)
|
||||
|
||||
if debug {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ func TestUpdate(t *testing.T) {
|
||||
s["bar"] = 1 // nolint:gomnd
|
||||
s["zonk"] = float64(1) // nolint:gomnd
|
||||
mockStater := &MockStater{}
|
||||
mockStater.On("Now", "http://foo.tld").Return(s)
|
||||
mockStater.On("Now", "http://foo.tld").Return(s, nil)
|
||||
|
||||
m := &Metrics{
|
||||
URL: "http://foo.tld",
|
||||
|
@ -10,7 +10,7 @@ type MockStater struct {
|
||||
}
|
||||
|
||||
// Now provides a mock function with given fields: url
|
||||
func (_m *MockStater) Now(url string) map[string]interface{} {
|
||||
func (_m *MockStater) Now(url string) (map[string]interface{}, error) {
|
||||
ret := _m.Called(url)
|
||||
|
||||
var r0 map[string]interface{}
|
||||
@ -22,5 +22,12 @@ func (_m *MockStater) Now(url string) map[string]interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(url)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user