Skip to content

Commit

Permalink
add prometheus support (#228)
Browse files Browse the repository at this point in the history
* add prometheus support

* refactored to manage own registry

* go mod tidy

* removed ProcessCollector as this is only supported on linux
  • Loading branch information
sarge authored and kelindar committed May 5, 2019
1 parent 49bd4a1 commit 7fd7c21
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 17 deletions.
7 changes: 2 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ require (
github.com/emitter-io/address v1.0.0
github.com/emitter-io/config v1.0.0
github.com/emitter-io/stats v1.0.1
github.com/golang/protobuf v0.0.0-20181005181728-ddf22928ea3c // indirect
github.com/golang/snappy v0.0.1
github.com/gorilla/websocket v1.4.0
github.com/kelindar/binary v1.0.1
github.com/kelindar/tcp v1.0.0
github.com/klauspost/compress v1.4.1 // indirect
github.com/klauspost/cpuid v1.2.0 // indirect
github.com/pkg/errors v0.0.0-20181008045315-2233dee583dc // indirect
github.com/prometheus/client_golang v0.9.2
github.com/stretchr/objx v0.1.1 // indirect
github.com/stretchr/testify v1.3.0
github.com/tidwall/btree v0.0.0-20170113224114-9876f1454cf0 // indirect
Expand All @@ -30,9 +30,6 @@ require (
github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563 // indirect
github.com/valyala/fasthttp v1.2.0
github.com/weaveworks/mesh v0.0.0-20190204141226-512bdb7b3cb7
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25
golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95 // indirect
golang.org/x/sys v0.0.0-20190302025703-b6889370fb10 // indirect
golang.org/x/text v0.3.0 // indirect
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734
gopkg.in/alexcesaro/statsd.v2 v2.0.0
)
36 changes: 24 additions & 12 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ github.com/aws/aws-sdk-go v1.17.9 h1:umGyqfZNxB4waFNvARXzBalEwoYz+8Cqk3xM45No9GI
github.com/aws/aws-sdk-go v1.17.9/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/axiomhq/hyperloglog v0.0.0-20181223111420-4b99d0c2c99e h1:190ugM9MsyFauTkR/UqcHG/mn5nmFe6SvHJqEHIrtrA=
github.com/axiomhq/hyperloglog v0.0.0-20181223111420-4b99d0c2c99e/go.mod h1:IOXAcuKIFq/mDyuQ4wyJuJ79XLMsmLM+5RdQ+vWrL7o=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/davecgh/go-spew v0.0.0-20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand All @@ -23,8 +25,8 @@ github.com/emitter-io/config v1.0.0/go.mod h1:IYK9R3556GTY93QphoHMCP65SwRpdo4KKh
github.com/emitter-io/stats v1.0.1 h1:w3McdlSNZsJWULe8q9sKpONrDxgFlpvqUFqztmjroxA=
github.com/emitter-io/stats v1.0.1/go.mod h1:4hKrGsT8bS+kMlTNsCyY1r2j8Jsq25U1+JpLkAtgPX4=
github.com/go-ini/ini v1.39.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/golang/protobuf v0.0.0-20181005181728-ddf22928ea3c h1:KgJw3nNJsnby+wgopcttaq/2qfhNJcVmygvYRGQ5Yc4=
github.com/golang/protobuf v0.0.0-20181005181728-ddf22928ea3c/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
Expand All @@ -49,10 +51,20 @@ github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e h1:+lIPJOWl+jSiJOc
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
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/pkg/errors v0.0.0-20181008045315-2233dee583dc h1:yqRW6E8qu5XzI4NpminLsmJvlskRLJzcBDNK+FWK1OI=
github.com/pkg/errors v0.0.0-20181008045315-2233dee583dc/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/stretchr/objx v0.0.0-20180825064932-ef50b0de2877 h1:Uk9JZ6L7yGoJ0V/gy05d5cSnPGf4bEP7AN1DeEJuX4E=
github.com/stretchr/objx v0.0.0-20180825064932-ef50b0de2877/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand Down Expand Up @@ -88,19 +100,19 @@ github.com/weaveworks/mesh v0.0.0-20190204141226-512bdb7b3cb7 h1:B/sLcPxyXuMHC+j
github.com/weaveworks/mesh v0.0.0-20190204141226-512bdb7b3cb7/go.mod h1:mcON9Ws1aW0crSErpXWp7U1ErCDEKliDX2OhVlbWRKk=
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e h1:IzypfodbhbnViNUO/MEh0FzCUooG97cIGfdggUrUSyU=
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 h1:jsG6UpNLt9iAsb0S2AGW28DveNzzgmbXR+ENoPjUeIU=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95 h1:fY7Dsw114eJN4boqzVSbpVHO6rTdhq6/GnXeu+PKnzU=
golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190302025703-b6889370fb10 h1:xQJI9OEiErEQ++DoXOHqEpzsGMrAv2Q2jyCpi7DmfpQ=
golang.org/x/sys v0.0.0-20190302025703-b6889370fb10/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
gopkg.in/alexcesaro/statsd.v2 v2.0.0 h1:FXkZSCZIH17vLCO5sO2UucTHsH9pc+17F6pl3JVCwMc=
gopkg.in/alexcesaro/statsd.v2 v2.0.0/go.mod h1:i0ubccKGzBVNBpdGV5MocxyA/XlLUJzA7SLonnE4drU=
1 change: 1 addition & 0 deletions internal/broker/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ func NewService(ctx context.Context, cfg *config.Config) (s *Service, err error)
monitor.NewNoop(),
monitor.NewHTTP(sampler),
monitor.NewStatsd(sampler, nodeName),
monitor.NewPrometheus(sampler, mux),
).(monitor.Storage)
logging.LogTarget("service", "configured monitoring sink", s.monitor.Name())

Expand Down
151 changes: 151 additions & 0 deletions internal/provider/monitor/prometheus.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package monitor

import (
"context"
"net/http"
"strings"
"time"

"github.com/emitter-io/emitter/internal/async"
"github.com/emitter-io/stats"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Noop implements Storage contract.
var _ Storage = new(Prometheus)

// Prometheus represents a storage which publishes stats to a statsd sink.
type Prometheus struct {
registry *prometheus.Registry // Prometheus registry
reader stats.Snapshotter // The reader which reads the snapshot of stats.
cancel context.CancelFunc // The cancellation function.
gauges map[string]prometheus.Gauge // The gauges created
histograms map[string]prometheus.Histogram // The histograms created
}

// NewPrometheus creates a new prometheus endpoint.
func NewPrometheus(snapshotter stats.Snapshotter, mux *http.ServeMux) *Prometheus {

// manage own prometheus registry
registry := prometheus.NewRegistry()
registry.MustRegister(prometheus.NewGoCollector())

mux.Handle("/metrics", promhttp.InstrumentMetricHandler(registry, promhttp.HandlerFor(registry, promhttp.HandlerOpts{})))

return &Prometheus{
registry: registry,
reader: snapshotter,
gauges: make(map[string]prometheus.Gauge, 0),
histograms: make(map[string]prometheus.Histogram, 0),
}
}

// Name returns the name of the provider.
func (p *Prometheus) Name() string {
return "prometheus"
}

// Configure configures the storage. The config parameter provided is
// loosely typed, since various storage mechanisms will require different
// configurations.
func (p *Prometheus) Configure(config map[string]interface{}) (err error) {

// Get the interval from the provider configuration
interval := defaultInterval
if v, ok := config["interval"]; ok {
if i, ok := v.(float64); ok {
interval = time.Duration(i) * time.Millisecond
}
}

p.cancel = async.Repeat(context.Background(), interval, p.write)

return
}

// Flush reads and writes stats into this stats sink.
func (p *Prometheus) write() {
// Create a snapshot and restore it straight away
snapshot := p.reader.Snapshot()
m, err := stats.Restore(snapshot)
if err != nil {
return
}

// Send the node and process-level metrics through
metrics := m.ToMap()
p.gauge(metrics, "node.peers")
p.gauge(metrics, "node.conns")
p.gauge(metrics, "node.subs")

for name := range metrics {
prefix := strings.Split(name, ".")[0]
switch prefix {
case "rcv", "send":
p.histogram(metrics, name)
}
}
}

// addGauge creates a gauge and maps it to a metric name
func (p *Prometheus) addGauge(metric string) prometheus.Gauge {
opts := prometheus.GaugeOpts{
Name: strings.Replace(metric, ".", "_", -1),
}

g := prometheus.NewGauge(opts)
if err := p.registry.Register(g); err != nil {
panic(err)
}

p.gauges[metric] = g

return g
}

func (p *Prometheus) addHistogram(metric string) prometheus.Histogram {
opts := prometheus.HistogramOpts{
Name: strings.Replace(metric, ".", "_", -1),
}
h := prometheus.NewHistogram(opts)
if err := p.registry.Register(h); err != nil {
panic(err)
}
p.histograms[metric] = h
return h
}

// sends the metric as a gauge
func (p *Prometheus) gauge(source map[string]stats.Snapshot, metric string) {
if v, ok := source[metric]; ok {
g, ok := p.gauges[metric]
if !ok {
g = p.addGauge(metric)
}
g.Set(float64(v.Max()))
}
}

// sends the metric as a histogram
func (p *Prometheus) histogram(source map[string]stats.Snapshot, metric string) {
if v, ok := source[metric]; ok {
for _, sample := range v.Sample {
h, ok := p.histograms[metric]
if !ok {
h = p.addHistogram(metric)
}
h.Observe(float64(sample))
}
}
}

// Close gracefully terminates the storage and ensures that every related
// resource is properly disposed.
func (p *Prometheus) Close() error {
if p.cancel != nil {
p.cancel()
}

return nil
}
84 changes: 84 additions & 0 deletions internal/provider/monitor/prometheus_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package monitor

import (
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"testing"

"github.com/emitter-io/stats"
"github.com/stretchr/testify/assert"
)

func TestPrometheus_HappyPath(t *testing.T) {
m := stats.New()
for i := int32(0); i < 100; i++ {
m.Measure("proc.test", i)
m.Measure("node.test", i)
m.Measure("rcv.test", i)
}

mux := http.NewServeMux()

s := NewPrometheus(m, mux)
defer s.Close()

err := s.Configure(map[string]interface{}{
"interval": 1000000.00,
})
assert.NoError(t, err)
assert.NotPanics(t, func() {
s.write()
})
}

func TestPrometheus_Request(t *testing.T) {

m := stats.New()
for i := int32(0); i < 100; i++ {
m.Measure("proc.test", i)
m.Measure("node.test", i)
m.Measure("rcv.test", i/10)
m.Measure("node.peers", 2)
m.Measure("node.conns", i)
m.Measure("node.subs", i)
}

mux := http.NewServeMux()
s := NewPrometheus(m, mux)
defer s.Close()

err := s.Configure(map[string]interface{}{
"interval": 1000000.00,
})

ts := httptest.NewServer(mux)
defer ts.Close()

res, err := http.Get(ts.URL + "/metrics")
if err != nil {
log.Fatal(err)
}
content, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
log.Fatal(err)
}

// assert gauges
assert.Contains(t, string(content), "node_peers 2")
assert.Contains(t, string(content), "node_subs 99")
assert.Contains(t, string(content), "node_conns 99")

// assert histograms
assert.Contains(t, string(content), "rcv_test_bucket{le=\"0.01\"} 10")
assert.Contains(t, string(content), "rcv_test_sum 450")
assert.Contains(t, string(content), "rcv_test_count 100")

// from InstrumentMetricHandler
assert.Contains(t, string(content), "promhttp_metric_handler_requests_total")

// from the NewGoCollector
assert.Contains(t, string(content), "go_threads")
}

0 comments on commit 7fd7c21

Please sign in to comment.