Commit b8f4faa2 authored by Janos Guljas's avatar Janos Guljas

add metrics

parent 638065c8
......@@ -18,13 +18,13 @@ Docker image `janos/bee`.
Execute the command terminals to start `node 1`:
```sh
bee start --api-addr :8501 --p2p-addr :30401
bee start --api-addr :8501 --p2p-addr :30401 --debug-api-addr :6061
```
Use one of the multiaddresses as bootnode for `node 2` in order to connect them:
```sh
bee start --api-addr :8502 --p2p-addr :30402 --bootnode /ip4/127.0.0.1/tcp/30401/p2p/QmT4TNB4cKYanUjdYodw1Cns8cuVaRVo24hHNYcT7JjkTB
bee start --api-addr :8502 --p2p-addr :30402 --debug-api-addr :6062 --bootnode /ip4/127.0.0.1/tcp/30401/p2p/QmT4TNB4cKYanUjdYodw1Cns8cuVaRVo24hHNYcT7JjkTB
```
Use the last part of `node 1` multiaddress to ping it using `node 2` by making an HTTP request to `localhost:{PORT2}/pingpong/{ID1}` like:
......
......@@ -76,14 +76,15 @@ func (c *command) initStartCmd() (err error) {
}
// API server
apiService := api.New(api.Options{
P2P: p2ps,
Pingpong: pingPong,
})
apiListener, err := net.Listen("tcp", c.config.GetString(optionNameAPIAddr))
if err != nil {
return fmt.Errorf("api listener: %w", err)
}
apiServer := &http.Server{Handler: api.New(api.Options{
P2P: p2ps,
Pingpong: pingPong,
})}
apiServer := &http.Server{Handler: apiService}
go func() {
cmd.Println("api address:", apiListener.Addr())
......@@ -93,20 +94,29 @@ func (c *command) initStartCmd() (err error) {
}
}()
// Debug API server
debugAPIListener, err := net.Listen("tcp", c.config.GetString(optionNameDebugAPIAddr))
if err != nil {
return fmt.Errorf("debug api listener: %w", err)
}
debugAPIServer := &http.Server{Handler: debugapi.New(debugapi.Options{})}
var debugAPIServer *http.Server
if addr := c.config.GetString(optionNameDebugAPIAddr); addr != "" {
// Debug API server
debugAPIService := debugapi.New(debugapi.Options{})
// register metrics from components
debugAPIService.MustRegisterMetrics(pingPong.Metrics()...)
debugAPIService.MustRegisterMetrics(apiService.Metrics()...)
debugAPIListener, err := net.Listen("tcp", addr)
if err != nil {
return fmt.Errorf("debug api listener: %w", err)
}
go func() {
cmd.Println("debug api address:", debugAPIListener.Addr())
debugAPIServer := &http.Server{Handler: debugAPIService}
if err := debugAPIServer.Serve(debugAPIListener); err != nil && err != http.ErrServerClosed {
log.Println("debug api server:", err)
}
}()
go func() {
cmd.Println("debug api address:", debugAPIListener.Addr())
if err := debugAPIServer.Serve(debugAPIListener); err != nil && err != http.ErrServerClosed {
log.Println("debug api server:", err)
}
}()
}
// Wait for termination or interrupt signals.
// We want to clean up things at the end.
......@@ -135,8 +145,10 @@ func (c *command) initStartCmd() (err error) {
log.Println("api server shutdown:", err)
}
if err := debugAPIServer.Shutdown(ctx); err != nil {
log.Println("debug api server shutdown:", err)
if debugAPIServer != nil {
if err := debugAPIServer.Shutdown(ctx); err != nil {
log.Println("debug api server shutdown:", err)
}
}
if err := p2ps.Close(); err != nil {
......
......@@ -18,6 +18,7 @@ require (
github.com/mitchellh/go-homedir v1.1.0
github.com/multiformats/go-multiaddr v0.2.0
github.com/multiformats/go-multistream v0.1.0
github.com/prometheus/client_golang v1.3.0
github.com/spf13/cobra v0.0.5
github.com/spf13/viper v1.6.1
resenje.org/web v0.4.0
......
......@@ -13,6 +13,7 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
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/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
......@@ -28,7 +29,9 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
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/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
......@@ -292,6 +295,7 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
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/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
......@@ -369,20 +373,24 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/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 h1:ElTg5tNp4DqfV7UQjDqv2+RJlNzsDtvNAWccbItceIE=
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
......
......@@ -5,11 +5,18 @@ import (
"github.com/janos/bee/pkg/p2p"
"github.com/janos/bee/pkg/pingpong"
"github.com/prometheus/client_golang/prometheus"
)
type Service interface {
http.Handler
Metrics() (cs []prometheus.Collector)
}
type server struct {
Options
http.Handler
metrics metrics
}
type Options struct {
......@@ -17,9 +24,10 @@ type Options struct {
Pingpong *pingpong.Service
}
func New(o Options) http.Handler {
func New(o Options) Service {
s := &server{
Options: o,
metrics: newMetrics(),
}
s.setupRouting()
......
package api
import (
"net/http"
"reflect"
"time"
"github.com/prometheus/client_golang/prometheus"
)
type metrics struct {
// all metrics fields must be exported
// to be able to return them by Metrics()
// using reflection
RequestCount prometheus.Counter
ResponseDuration prometheus.Histogram
PingRequestCount prometheus.Counter
}
func newMetrics() (m metrics) {
return metrics{
RequestCount: prometheus.NewCounter(prometheus.CounterOpts{
Name: "api_request_count",
Help: "Number of API requests.",
}),
ResponseDuration: prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "api_response_duration_seconds",
Help: "Histogram of API response durations.",
Buckets: []float64{0.01, 0.1, 0.25, 0.5, 1, 2.5, 5, 10},
}),
PingRequestCount: prometheus.NewCounter(prometheus.CounterOpts{
Name: "api_ping_request_count",
Help: "Number HTTP API ping requests.",
}),
}
}
func (s *server) Metrics() (cs []prometheus.Collector) {
v := reflect.Indirect(reflect.ValueOf(s.metrics))
for i := 0; i < v.NumField(); i++ {
if !v.Field(i).CanInterface() {
continue
}
if u, ok := v.Field(i).Interface().(prometheus.Collector); ok {
cs = append(cs, u)
}
}
return cs
}
func (s *server) pageviewMetricsHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
s.metrics.RequestCount.Inc()
h.ServeHTTP(w, r)
s.metrics.ResponseDuration.Observe(time.Since(start).Seconds())
})
}
......@@ -17,6 +17,7 @@ func (s *server) pingpongHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "ping error", peerID, err)
return
}
s.metrics.PingRequestCount.Inc()
fmt.Fprintln(w, "RTT", rtt)
}
......@@ -20,6 +20,7 @@ func (s *server) setupRouting() {
s.Handler = web.ChainHandlers(
handlers.CompressHandler,
s.pageviewMetricsHandler,
web.FinalHandler(baseRouter),
)
}
......@@ -2,18 +2,28 @@ package debugapi
import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
)
type Service interface {
http.Handler
MustRegisterMetrics(cs ...prometheus.Collector)
}
type server struct {
Options
http.Handler
metricsRegistry *prometheus.Registry
}
type Options struct{}
func New(o Options) http.Handler {
func New(o Options) Service {
s := &server{
Options: o,
Options: o,
metricsRegistry: newMetricsRegistry(),
}
s.setupRouting()
......
package debugapi
import (
"github.com/janos/bee"
"github.com/prometheus/client_golang/prometheus"
)
func newMetricsRegistry() (r *prometheus.Registry) {
r = prometheus.NewRegistry()
// register standard metrics
r.MustRegister(
prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}),
prometheus.NewGoCollector(),
prometheus.NewGauge(prometheus.GaugeOpts{
Name: "bee_info",
Help: "Bee information.",
ConstLabels: prometheus.Labels{
"version": bee.Version,
},
}),
)
return r
}
func (s *server) MustRegisterMetrics(cs ...prometheus.Collector) {
s.metricsRegistry.MustRegister(cs...)
}
......@@ -6,12 +6,18 @@ import (
"net/http/pprof"
"github.com/gorilla/handlers"
"github.com/prometheus/client_golang/prometheus/promhttp"
"resenje.org/web"
)
func (s *server) setupRouting() {
internalBaseRouter := http.NewServeMux()
internalBaseRouter.Handle("/metrics", promhttp.InstrumentMetricHandler(
s.metricsRegistry,
promhttp.HandlerFor(s.metricsRegistry, promhttp.HandlerOpts{}),
))
internalRouter := http.NewServeMux()
internalBaseRouter.Handle("/", web.ChainHandlers(
handlers.CompressHandler,
......
package pingpong
import (
"reflect"
"github.com/prometheus/client_golang/prometheus"
)
type metrics struct {
// all metrics fields must be exported
// to be able to return them by Metrics()
// using reflection
PingSentCount prometheus.Counter
PongSentCount prometheus.Counter
PingReceivedCount prometheus.Counter
PongReceivedCount prometheus.Counter
}
func newMetrics() (m metrics) {
return metrics{
PingSentCount: prometheus.NewCounter(prometheus.CounterOpts{
Name: "pingpong_ping_sent_count",
Help: "Number ping requests sent.",
}),
PongSentCount: prometheus.NewCounter(prometheus.CounterOpts{
Name: "pingpong_pong_sent_count",
Help: "Number of pong responses sent.",
}),
PingReceivedCount: prometheus.NewCounter(prometheus.CounterOpts{
Name: "pingpong_ping_received_count",
Help: "Number ping requests received.",
}),
PongReceivedCount: prometheus.NewCounter(prometheus.CounterOpts{
Name: "pingpong_pong_received_count",
Help: "Number of pong responses received.",
}),
}
}
func (s *Service) Metrics() (cs []prometheus.Collector) {
v := reflect.Indirect(reflect.ValueOf(s.metrics))
for i := 0; i < v.NumField(); i++ {
if !v.Field(i).CanInterface() {
continue
}
if u, ok := v.Field(i).Interface().(prometheus.Collector); ok {
cs = append(cs, u)
}
}
return cs
}
......@@ -21,10 +21,14 @@ const (
type Service struct {
streamer p2p.Streamer
metrics metrics
}
func New(streamer p2p.Streamer) *Service {
return &Service{streamer: streamer}
return &Service{
streamer: streamer,
metrics: newMetrics(),
}
}
func (s *Service) Protocol() p2p.ProtocolSpec {
......@@ -54,6 +58,7 @@ func (s *Service) Handler(p p2p.Peer) {
return
}
log.Printf("got ping: %q\n", ping.Greeting)
s.metrics.PingReceivedCount.Inc()
if err := w.WriteMsg(&Pong{
Response: "{" + ping.Greeting + "}",
......@@ -61,6 +66,7 @@ func (s *Service) Handler(p p2p.Peer) {
log.Printf("pingpong handler: write message: %v\n", err)
return
}
s.metrics.PongSentCount.Inc()
}
}
......@@ -81,6 +87,7 @@ func (s *Service) Ping(ctx context.Context, peerID string, msgs ...string) (rtt
}); err != nil {
return 0, fmt.Errorf("stream write: %w", err)
}
s.metrics.PingSentCount.Inc()
if err := r.ReadMsg(&pong); err != nil {
if err == io.EOF {
......@@ -90,6 +97,7 @@ func (s *Service) Ping(ctx context.Context, peerID string, msgs ...string) (rtt
}
log.Printf("got pong: %q\n", pong.Response)
s.metrics.PongReceivedCount.Inc()
}
return time.Since(start) / time.Duration(len(msgs)), nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment