From caec086c2853a6f4690d18cce360e94cbd088722 Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Tue, 5 Apr 2016 13:23:00 -0400 Subject: [PATCH] metrics: add prometheus back With a proper IpfsCollector object and tests, this time. The collector object makes it easy to add further metrics, like e.g. bitswap wants/provs. License: MIT Signed-off-by: Lars Gierth --- cmd/ipfs/daemon.go | 11 ++++++-- core/corehttp/metrics.go | 53 +++++++++++++++++++++++++++++++++++ core/corehttp/metrics_test.go | 46 ++++++++++++++++++++++++++++++ core/corehttp/prometheus.go | 25 ----------------- package.json | 7 ++++- 5 files changed, 113 insertions(+), 29 deletions(-) create mode 100644 core/corehttp/metrics.go create mode 100644 core/corehttp/metrics_test.go delete mode 100644 core/corehttp/prometheus.go diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index 115b86d60..df1b508c4 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -27,6 +27,7 @@ import ( util "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" conn "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net/conn" peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" + prometheus "gx/ipfs/QmdhsRK1EK2fvAz2i2SH5DEfkL6seDuyMYEsxKa9Braim3/client_golang/prometheus" ) const ( @@ -314,6 +315,10 @@ func daemonFunc(req cmds.Request, res cmds.Response) { return } + // initialize metrics collector + prometheus.MustRegisterOrGet(&corehttp.IpfsNodeCollector{Node: node}) + prometheus.EnableCollectChecks(true) + fmt.Printf("Daemon is ready\n") // collect long-running errors and block for shutdown // TODO(cryptix): our fuse currently doesnt follow this pattern for graceful shutdown @@ -376,15 +381,15 @@ func serveHTTPApi(req cmds.Request) (error, <-chan error) { }, }) var opts = []corehttp.ServeOption{ - corehttp.PrometheusCollectorOption("api"), + corehttp.MetricsCollectionOption("api"), corehttp.CommandsOption(*req.InvocContext()), corehttp.WebUIOption, apiGw.ServeOption(), corehttp.VersionOption(), defaultMux("/debug/vars"), defaultMux("/debug/pprof/"), + corehttp.MetricsScrapingOption("/debug/metrics/prometheus"), corehttp.LogOption(), - corehttp.PrometheusOption("/debug/metrics/prometheus"), } if len(cfg.Gateway.RootRedirect) > 0 { @@ -455,7 +460,7 @@ func serveHTTPGateway(req cmds.Request) (error, <-chan error) { } var opts = []corehttp.ServeOption{ - corehttp.PrometheusCollectorOption("gateway"), + corehttp.MetricsCollectionOption("gateway"), corehttp.CommandsROOption(*req.InvocContext()), corehttp.VersionOption(), corehttp.IPNSHostnameOption(), diff --git a/core/corehttp/metrics.go b/core/corehttp/metrics.go new file mode 100644 index 000000000..5d00a3c4c --- /dev/null +++ b/core/corehttp/metrics.go @@ -0,0 +1,53 @@ +package corehttp + +import ( + "net" + "net/http" + + prometheus "gx/ipfs/QmdhsRK1EK2fvAz2i2SH5DEfkL6seDuyMYEsxKa9Braim3/client_golang/prometheus" + + core "github.com/ipfs/go-ipfs/core" +) + +// This 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) { + mux.Handle(path, prometheus.UninstrumentedHandler()) + return mux, nil + } +} + +// This adds collection of net/http-related metrics +func MetricsCollectionOption(handlerName string) ServeOption { + return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { + childMux := http.NewServeMux() + mux.HandleFunc("/", prometheus.InstrumentHandler(handlerName, childMux)) + return childMux, nil + } +} + +var ( + peersTotalMetric = prometheus.NewDesc( + prometheus.BuildFQName("ipfs", "p2p", "peers_total"), + "Number of connected peers", nil, nil) +) + +type IpfsNodeCollector struct { + Node *core.IpfsNode +} + +func (_ IpfsNodeCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- peersTotalMetric +} + +func (c IpfsNodeCollector) Collect(ch chan<- prometheus.Metric) { + ch <- prometheus.MustNewConstMetric( + peersTotalMetric, + prometheus.GaugeValue, + c.PeersTotalValue(), + ) +} + +func (c IpfsNodeCollector) PeersTotalValue() float64 { + return float64(len(c.Node.PeerHost.Network().Conns())) +} diff --git a/core/corehttp/metrics_test.go b/core/corehttp/metrics_test.go new file mode 100644 index 000000000..da6146392 --- /dev/null +++ b/core/corehttp/metrics_test.go @@ -0,0 +1,46 @@ +package corehttp + +import ( + "testing" + "time" + + core "github.com/ipfs/go-ipfs/core" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + bhost "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/host/basic" + inet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net" + testutil "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/test/util" +) + +// This test is based on go-libp2p/p2p/net/swarm.TestConnectednessCorrect +// It builds 4 nodes and connects them, one being the sole center. +// Then it checks that the center reports the correct number of peers. +func TestPeersTotal(t *testing.T) { + ctx := context.Background() + + hosts := make([]*bhost.BasicHost, 4) + for i := 0; i < 4; i++ { + hosts[i] = testutil.GenHostSwarm(t, ctx) + } + + dial := func(a, b inet.Network) { + testutil.DivulgeAddresses(b, a) + if _, err := a.DialPeer(ctx, b.LocalPeer()); err != nil { + t.Fatalf("Failed to dial: %s", err) + } + } + + dial(hosts[0].Network(), hosts[1].Network()) + dial(hosts[0].Network(), hosts[2].Network()) + dial(hosts[0].Network(), hosts[3].Network()) + + // there's something wrong with dial, i think. it's not finishing + // completely. there must be some async stuff. + <-time.After(100 * time.Millisecond) + + node := &core.IpfsNode{PeerHost: hosts[0]} + collector := IpfsNodeCollector{Node: node} + actual := collector.PeersTotalValue() + if actual != 3 { + t.Fatalf("expected 3 peers, got %d", int(actual)) + } +} diff --git a/core/corehttp/prometheus.go b/core/corehttp/prometheus.go deleted file mode 100644 index 2605e6cbe..000000000 --- a/core/corehttp/prometheus.go +++ /dev/null @@ -1,25 +0,0 @@ -package corehttp - -import ( - "net" - "net/http" - - prom "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus" - - "github.com/ipfs/go-ipfs/core" -) - -func PrometheusOption(path string) ServeOption { - return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { - mux.Handle(path, prom.UninstrumentedHandler()) - return mux, nil - } -} - -func PrometheusCollectorOption(handlerName string) ServeOption { - return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { - childMux := http.NewServeMux() - mux.HandleFunc("/", prom.InstrumentHandler(handlerName, childMux)) - return childMux, nil - } -} diff --git a/package.json b/package.json index 0db30b448..6f868ae10 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,11 @@ "hash": "QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV", "name": "gogo-protobuf", "version": "0.0.0" + }, + { + "hash": "QmdhsRK1EK2fvAz2i2SH5DEfkL6seDuyMYEsxKa9Braim3", + "name": "client_golang", + "version": "0.0.0" } ], "gxVersion": "0.4.0", @@ -39,4 +44,4 @@ "license": "", "name": "go-ipfs", "version": "0.4.0" -} \ No newline at end of file +}