Skip to content

Commit

Permalink
Fix errors with metrics when there are multiple listeners
Browse files Browse the repository at this point in the history
When there are multiple listeners configured for Addresses.API, serving metrics results in an errors: "<metric> was collected before with the same name and label values".

This PR fixes this by maintaining a global map of metrics handlers, and only creating and reginstering them once. The same metrics handlers are provided to the mux for every listener.

Fixes #9891
Fixes #9397

Unblocks #9637
  • Loading branch information
gammazero committed Aug 21, 2024
1 parent 263edb2 commit dddc915
Showing 1 changed file with 46 additions and 16 deletions.
62 changes: 46 additions & 16 deletions core/corehttp/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package corehttp
import (
"net"
"net/http"
"sync"
"time"

core "github.com/ipfs/kubo/core"
Expand All @@ -14,6 +15,9 @@ import (
promhttp "github.com/prometheus/client_golang/prometheus/promhttp"
)

var ocHandlers = map[string]http.Handler{}
var ocMutex sync.Mutex

// MetricsScrapingOption adds the scraping endpoint which Prometheus uses to fetch metrics.
func MetricsScrapingOption(path string) ServeOption {
return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
Expand All @@ -27,25 +31,33 @@ func MetricsOpenCensusCollectionOption() ServeOption {
return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
log.Info("Init OpenCensus")

promRegistry := prometheus.NewRegistry()
pe, err := ocprom.NewExporter(ocprom.Options{
Namespace: "ipfs_oc",
Registry: promRegistry,
OnError: func(err error) {
log.Errorw("OC ERROR", "error", err)
},
})
if err != nil {
return nil, err
}
ocMutex.Lock()
defer ocMutex.Unlock()
ocHandler, ok := ocHandlers["ipfs_oc"]
if !ok {
promRegistry := prometheus.NewRegistry()
pe, err := ocprom.NewExporter(ocprom.Options{
Namespace: "ipfs_oc",
Registry: promRegistry,
OnError: func(err error) {
log.Errorw("OC ERROR", "error", err)
},
})
if err != nil {
return nil, err
}

// register prometheus with opencensus
view.RegisterExporter(pe)
view.SetReportingPeriod(2 * time.Second)
// register prometheus with opencensus
view.RegisterExporter(pe)
view.SetReportingPeriod(2 * time.Second)

ocHandler = pe
ocHandlers["ipfs_oc"] = ocHandler
}

// Construct the mux
zpages.Handle(mux, "/debug/metrics/oc/debugz")
mux.Handle("/debug/metrics/oc", pe)
mux.Handle("/debug/metrics/oc", ocHandler)

return mux, nil
}
Expand All @@ -58,6 +70,13 @@ func MetricsOpenCensusDefaultPrometheusRegistry() ServeOption {
return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
log.Info("Init OpenCensus with default prometheus registry")

ocMutex.Lock()
defer ocMutex.Unlock()
_, ok := ocHandlers["ipfs_oc"]
if ok {
return mux, nil
}

pe, err := ocprom.NewExporter(ocprom.Options{
Registry: prometheus.DefaultRegisterer.(*prometheus.Registry),
OnError: func(err error) {
Expand All @@ -71,13 +90,23 @@ func MetricsOpenCensusDefaultPrometheusRegistry() ServeOption {
// register prometheus with opencensus
view.RegisterExporter(pe)

ocHandlers["ipfs_oc"] = pe

return mux, nil
}
}

// MetricsCollectionOption adds collection of net/http-related metrics.
func MetricsCollectionOption(handlerName string) ServeOption {
return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
ocMutex.Lock()
defer ocMutex.Unlock()
promMux, ok := ocHandlers["ipfs_oc"]
if ok {
mux.Handle("/", promMux)
return promMux.(*http.ServeMux), nil
}

// Adapted from github.com/prometheus/client_golang/prometheus/http.go
// Work around https://github.com/prometheus/client_golang/pull/311
opts := prometheus.SummaryOpts{
Expand Down Expand Up @@ -140,13 +169,14 @@ func MetricsCollectionOption(handlerName string) ServeOption {

// Construct the mux
childMux := http.NewServeMux()
var promMux http.Handler = childMux
promMux = childMux
promMux = promhttp.InstrumentHandlerResponseSize(resSz, promMux)
promMux = promhttp.InstrumentHandlerRequestSize(reqSz, promMux)
promMux = promhttp.InstrumentHandlerDuration(reqDur, promMux)
promMux = promhttp.InstrumentHandlerCounter(reqCnt, promMux)
mux.Handle("/", promMux)

ocHandlers["prom_mux"] = promMux
return childMux, nil
}
}
Expand Down

0 comments on commit dddc915

Please sign in to comment.