Add prometheus metrics
This commit is contained in:
parent
a20a2d205f
commit
9a0851b7a1
@ -469,6 +469,8 @@ func blockbookAppInfoMetric(db *db.RocksDB, chain bchain.BlockChain, txCache *db
|
|||||||
"backend_version": si.Backend.Version,
|
"backend_version": si.Backend.Version,
|
||||||
"backend_subversion": si.Backend.Subversion,
|
"backend_subversion": si.Backend.Subversion,
|
||||||
"backend_protocol_version": si.Backend.ProtocolVersion}).Set(float64(0))
|
"backend_protocol_version": si.Backend.ProtocolVersion}).Set(float64(0))
|
||||||
|
metrics.BackendBestHeight.Set(float64(si.Backend.Blocks))
|
||||||
|
metrics.BlockbookBestHeight.Set(float64(si.Blockbook.BestHeight))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,25 +8,30 @@ import (
|
|||||||
|
|
||||||
// Metrics holds prometheus collectors for various metrics collected by Blockbook
|
// Metrics holds prometheus collectors for various metrics collected by Blockbook
|
||||||
type Metrics struct {
|
type Metrics struct {
|
||||||
SocketIORequests *prometheus.CounterVec
|
SocketIORequests *prometheus.CounterVec
|
||||||
SocketIOSubscribes *prometheus.CounterVec
|
SocketIOSubscribes *prometheus.CounterVec
|
||||||
SocketIOClients prometheus.Gauge
|
SocketIOClients prometheus.Gauge
|
||||||
SocketIOReqDuration *prometheus.HistogramVec
|
SocketIOReqDuration *prometheus.HistogramVec
|
||||||
WebsocketRequests *prometheus.CounterVec
|
WebsocketRequests *prometheus.CounterVec
|
||||||
WebsocketSubscribes *prometheus.CounterVec
|
WebsocketSubscribes *prometheus.CounterVec
|
||||||
WebsocketClients prometheus.Gauge
|
WebsocketClients prometheus.Gauge
|
||||||
WebsocketReqDuration *prometheus.HistogramVec
|
WebsocketReqDuration *prometheus.HistogramVec
|
||||||
IndexResyncDuration prometheus.Histogram
|
IndexResyncDuration prometheus.Histogram
|
||||||
MempoolResyncDuration prometheus.Histogram
|
MempoolResyncDuration prometheus.Histogram
|
||||||
TxCacheEfficiency *prometheus.CounterVec
|
TxCacheEfficiency *prometheus.CounterVec
|
||||||
RPCLatency *prometheus.HistogramVec
|
RPCLatency *prometheus.HistogramVec
|
||||||
IndexResyncErrors *prometheus.CounterVec
|
IndexResyncErrors *prometheus.CounterVec
|
||||||
IndexDBSize prometheus.Gauge
|
IndexDBSize prometheus.Gauge
|
||||||
ExplorerViews *prometheus.CounterVec
|
ExplorerViews *prometheus.CounterVec
|
||||||
MempoolSize prometheus.Gauge
|
MempoolSize prometheus.Gauge
|
||||||
DbColumnRows *prometheus.GaugeVec
|
DbColumnRows *prometheus.GaugeVec
|
||||||
DbColumnSize *prometheus.GaugeVec
|
DbColumnSize *prometheus.GaugeVec
|
||||||
BlockbookAppInfo *prometheus.GaugeVec
|
BlockbookAppInfo *prometheus.GaugeVec
|
||||||
|
BackendBestHeight prometheus.Gauge
|
||||||
|
BlockbookBestHeight prometheus.Gauge
|
||||||
|
ExplorerPendingRequests *prometheus.GaugeVec
|
||||||
|
WebsocketPendingRequests *prometheus.GaugeVec
|
||||||
|
SocketIOPendingRequests *prometheus.GaugeVec
|
||||||
}
|
}
|
||||||
|
|
||||||
// Labels represents a collection of label name -> value mappings.
|
// Labels represents a collection of label name -> value mappings.
|
||||||
@ -187,6 +192,44 @@ func GetMetrics(coin string) (*Metrics, error) {
|
|||||||
},
|
},
|
||||||
[]string{"blockbook_version", "blockbook_commit", "blockbook_buildtime", "backend_version", "backend_subversion", "backend_protocol_version"},
|
[]string{"blockbook_version", "blockbook_commit", "blockbook_buildtime", "backend_version", "backend_subversion", "backend_protocol_version"},
|
||||||
)
|
)
|
||||||
|
metrics.BlockbookBestHeight = prometheus.NewGauge(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "blockbook_best_height",
|
||||||
|
Help: "Block height in Blockbook",
|
||||||
|
ConstLabels: Labels{"coin": coin},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
metrics.BackendBestHeight = prometheus.NewGauge(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "blockbook_backend_best_height",
|
||||||
|
Help: "Block height in backend",
|
||||||
|
ConstLabels: Labels{"coin": coin},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
metrics.ExplorerPendingRequests = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "blockbook_explorer_pending_reqests",
|
||||||
|
Help: "Number of unfinished requests in explorer interface",
|
||||||
|
ConstLabels: Labels{"coin": coin},
|
||||||
|
},
|
||||||
|
[]string{"method"},
|
||||||
|
)
|
||||||
|
metrics.WebsocketPendingRequests = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "blockbook_websocket_pending_reqests",
|
||||||
|
Help: "Number of unfinished requests in websocket interface",
|
||||||
|
ConstLabels: Labels{"coin": coin},
|
||||||
|
},
|
||||||
|
[]string{"method"},
|
||||||
|
)
|
||||||
|
metrics.SocketIOPendingRequests = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "blockbook_socketio_pending_reqests",
|
||||||
|
Help: "Number of unfinished requests in socketio interface",
|
||||||
|
ConstLabels: Labels{"coin": coin},
|
||||||
|
},
|
||||||
|
[]string{"method"},
|
||||||
|
)
|
||||||
|
|
||||||
v := reflect.ValueOf(metrics)
|
v := reflect.ValueOf(metrics)
|
||||||
for i := 0; i < v.NumField(); i++ {
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
|||||||
@ -95,6 +95,8 @@ func (w *SyncWorker) ResyncIndex(onNewBlock bchain.OnNewBlockFunc, initialSync b
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
w.is.FinishedSync(bh)
|
w.is.FinishedSync(bh)
|
||||||
}
|
}
|
||||||
|
w.metrics.BackendBestHeight.Set(float64(w.is.BackendInfo.Blocks))
|
||||||
|
w.metrics.BlockbookBestHeight.Set(float64(bh))
|
||||||
return err
|
return err
|
||||||
case errSynced:
|
case errSynced:
|
||||||
// this is not actually error but flag that resync wasn't necessary
|
// this is not actually error but flag that resync wasn't necessary
|
||||||
@ -226,6 +228,7 @@ func (w *SyncWorker) connectBlocks(onNewBlock bchain.OnNewBlockFunc, initialSync
|
|||||||
if onNewBlock != nil {
|
if onNewBlock != nil {
|
||||||
onNewBlock(res.block.Hash, res.block.Height)
|
onNewBlock(res.block.Hash, res.block.Height)
|
||||||
}
|
}
|
||||||
|
w.metrics.BlockbookBestHeight.Set(float64(res.block.Height))
|
||||||
if res.block.Height > 0 && res.block.Height%1000 == 0 {
|
if res.block.Height > 0 && res.block.Height%1000 == 0 {
|
||||||
glog.Info("connected block ", res.block.Height, " ", res.block.Hash)
|
glog.Info("connected block ", res.block.Height, " ", res.block.Hash)
|
||||||
}
|
}
|
||||||
@ -377,6 +380,7 @@ ConnectLoop:
|
|||||||
}
|
}
|
||||||
hch <- hashHeight{hash, h}
|
hch <- hashHeight{hash, h}
|
||||||
if h > 0 && h%1000 == 0 {
|
if h > 0 && h%1000 == 0 {
|
||||||
|
w.metrics.BlockbookBestHeight.Set(float64(h))
|
||||||
glog.Info("connecting block ", h, " ", hash, ", elapsed ", time.Since(start), " ", w.db.GetAndResetConnectBlockStats())
|
glog.Info("connecting block ", h, " ", hash, ", elapsed ", time.Since(start), " ", w.db.GetAndResetConnectBlockStats())
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -258,7 +258,13 @@ func joinURL(base string, part string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getFunctionName(i interface{}) string {
|
func getFunctionName(i interface{}) string {
|
||||||
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
|
name := runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
|
||||||
|
start := strings.LastIndex(name, ".")
|
||||||
|
end := strings.LastIndex(name, "-")
|
||||||
|
if start > 0 && end > start {
|
||||||
|
name = name[start+1 : end]
|
||||||
|
}
|
||||||
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublicServer) jsonHandler(handler func(r *http.Request, apiVersion int) (interface{}, error), apiVersion int) func(w http.ResponseWriter, r *http.Request) {
|
func (s *PublicServer) jsonHandler(handler func(r *http.Request, apiVersion int) (interface{}, error), apiVersion int) func(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -266,12 +272,13 @@ func (s *PublicServer) jsonHandler(handler func(r *http.Request, apiVersion int)
|
|||||||
Text string `json:"error"`
|
Text string `json:"error"`
|
||||||
HTTPStatus int `json:"-"`
|
HTTPStatus int `json:"-"`
|
||||||
}
|
}
|
||||||
|
handlerName := getFunctionName(handler)
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var data interface{}
|
var data interface{}
|
||||||
var err error
|
var err error
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := recover(); e != nil {
|
if e := recover(); e != nil {
|
||||||
glog.Error(getFunctionName(handler), " recovered from panic: ", e)
|
glog.Error(handlerName, " recovered from panic: ", e)
|
||||||
debug.PrintStack()
|
debug.PrintStack()
|
||||||
if s.debug {
|
if s.debug {
|
||||||
data = jsonError{fmt.Sprint("Internal server error: recovered from panic ", e), http.StatusInternalServerError}
|
data = jsonError{fmt.Sprint("Internal server error: recovered from panic ", e), http.StatusInternalServerError}
|
||||||
@ -287,7 +294,9 @@ func (s *PublicServer) jsonHandler(handler func(r *http.Request, apiVersion int)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Warning("json encode ", err)
|
glog.Warning("json encode ", err)
|
||||||
}
|
}
|
||||||
|
s.metrics.ExplorerPendingRequests.With((common.Labels{"method": handlerName})).Dec()
|
||||||
}()
|
}()
|
||||||
|
s.metrics.ExplorerPendingRequests.With((common.Labels{"method": handlerName})).Inc()
|
||||||
data, err = handler(r, apiVersion)
|
data, err = handler(r, apiVersion)
|
||||||
if err != nil || data == nil {
|
if err != nil || data == nil {
|
||||||
if apiErr, ok := err.(*api.APIError); ok {
|
if apiErr, ok := err.(*api.APIError); ok {
|
||||||
@ -298,7 +307,7 @@ func (s *PublicServer) jsonHandler(handler func(r *http.Request, apiVersion int)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error(getFunctionName(handler), " error: ", err)
|
glog.Error(handlerName, " error: ", err)
|
||||||
}
|
}
|
||||||
if s.debug {
|
if s.debug {
|
||||||
if data != nil {
|
if data != nil {
|
||||||
@ -332,13 +341,14 @@ func (s *PublicServer) newTemplateDataWithError(text string) *TemplateData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublicServer) htmlTemplateHandler(handler func(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error)) func(w http.ResponseWriter, r *http.Request) {
|
func (s *PublicServer) htmlTemplateHandler(handler func(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error)) func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
handlerName := getFunctionName(handler)
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var t tpl
|
var t tpl
|
||||||
var data *TemplateData
|
var data *TemplateData
|
||||||
var err error
|
var err error
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := recover(); e != nil {
|
if e := recover(); e != nil {
|
||||||
glog.Error(getFunctionName(handler), " recovered from panic: ", e)
|
glog.Error(handlerName, " recovered from panic: ", e)
|
||||||
debug.PrintStack()
|
debug.PrintStack()
|
||||||
t = errorInternalTpl
|
t = errorInternalTpl
|
||||||
if s.debug {
|
if s.debug {
|
||||||
@ -358,7 +368,9 @@ func (s *PublicServer) htmlTemplateHandler(handler func(w http.ResponseWriter, r
|
|||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.metrics.ExplorerPendingRequests.With((common.Labels{"method": handlerName})).Dec()
|
||||||
}()
|
}()
|
||||||
|
s.metrics.ExplorerPendingRequests.With((common.Labels{"method": handlerName})).Inc()
|
||||||
if s.debug {
|
if s.debug {
|
||||||
// reload templates on each request
|
// reload templates on each request
|
||||||
// to reflect changes during development
|
// to reflect changes during development
|
||||||
@ -375,7 +387,7 @@ func (s *PublicServer) htmlTemplateHandler(handler func(w http.ResponseWriter, r
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error(getFunctionName(handler), " error: ", err)
|
glog.Error(handlerName, " error: ", err)
|
||||||
}
|
}
|
||||||
if s.debug {
|
if s.debug {
|
||||||
data = s.newTemplateDataWithError(fmt.Sprintf("Internal server error: %v, data %+v", err, data))
|
data = s.newTemplateDataWithError(fmt.Sprintf("Internal server error: %v, data %+v", err, data))
|
||||||
|
|||||||
@ -169,9 +169,11 @@ func (s *SocketIoServer) onMessage(c *gosocketio.Channel, req map[string]json.Ra
|
|||||||
e.Error.Message = "Internal error"
|
e.Error.Message = "Internal error"
|
||||||
rv = e
|
rv = e
|
||||||
}
|
}
|
||||||
|
s.metrics.SocketIOPendingRequests.With((common.Labels{"method": method})).Dec()
|
||||||
}()
|
}()
|
||||||
t := time.Now()
|
t := time.Now()
|
||||||
params := req["params"]
|
params := req["params"]
|
||||||
|
s.metrics.SocketIOPendingRequests.With((common.Labels{"method": method})).Inc()
|
||||||
defer s.metrics.SocketIOReqDuration.With(common.Labels{"method": method}).Observe(float64(time.Since(t)) / 1e3) // in microseconds
|
defer s.metrics.SocketIOReqDuration.With(common.Labels{"method": method}).Observe(float64(time.Since(t)) / 1e3) // in microseconds
|
||||||
f, ok := onMessageHandlers[method]
|
f, ok := onMessageHandlers[method]
|
||||||
if ok {
|
if ok {
|
||||||
|
|||||||
@ -436,8 +436,10 @@ func (s *WebsocketServer) onRequest(c *websocketChannel, req *websocketReq) {
|
|||||||
Data: data,
|
Data: data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
s.metrics.WebsocketPendingRequests.With((common.Labels{"method": req.Method})).Dec()
|
||||||
}()
|
}()
|
||||||
t := time.Now()
|
t := time.Now()
|
||||||
|
s.metrics.WebsocketPendingRequests.With((common.Labels{"method": req.Method})).Inc()
|
||||||
defer s.metrics.WebsocketReqDuration.With(common.Labels{"method": req.Method}).Observe(float64(time.Since(t)) / 1e3) // in microseconds
|
defer s.metrics.WebsocketReqDuration.With(common.Labels{"method": req.Method}).Observe(float64(time.Since(t)) / 1e3) // in microseconds
|
||||||
f, ok := requestHandlers[req.Method]
|
f, ok := requestHandlers[req.Method]
|
||||||
if ok {
|
if ok {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user