master
go 203 lines 6.1 KB
Raw
1 package corehttp
2
3 import (
4 "net"
5 "net/http"
6 "time"
7
8 core "github.com/ipfs/kubo/core"
9 "go.opencensus.io/stats/view"
10 "go.opencensus.io/zpages"
11
12 ocprom "contrib.go.opencensus.io/exporter/prometheus"
13 prometheus "github.com/prometheus/client_golang/prometheus"
14 promhttp "github.com/prometheus/client_golang/prometheus/promhttp"
15 )
16
17 // MetricsScrapingOption adds the scraping endpoint which Prometheus uses to fetch metrics.
18 func MetricsScrapingOption(path string) ServeOption {
19 return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
20 mux.Handle(path, promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{}))
21 return mux, nil
22 }
23 }
24
25 // This adds collection of OpenCensus metrics
26 func MetricsOpenCensusCollectionOption() ServeOption {
27 return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
28 log.Info("Init OpenCensus")
29
30 promRegistry := prometheus.NewRegistry()
31 pe, err := ocprom.NewExporter(ocprom.Options{
32 Namespace: "ipfs_oc",
33 Registry: promRegistry,
34 OnError: func(err error) {
35 log.Errorw("OC ERROR", "error", err)
36 },
37 })
38 if err != nil {
39 return nil, err
40 }
41
42 // register prometheus with opencensus
43 view.RegisterExporter(pe)
44 view.SetReportingPeriod(2 * time.Second)
45
46 // Construct the mux
47 zpages.Handle(mux, "/debug/metrics/oc/debugz")
48 mux.Handle("/debug/metrics/oc", pe)
49
50 return mux, nil
51 }
52 }
53
54 // MetricsOpenCensusDefaultPrometheusRegistry registers the default prometheus
55 // registry as an exporter to OpenCensus metrics. This means that OpenCensus
56 // metrics will show up in the prometheus metrics endpoint
57 func MetricsOpenCensusDefaultPrometheusRegistry() ServeOption {
58 return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
59 log.Info("Init OpenCensus with default prometheus registry")
60
61 pe, err := ocprom.NewExporter(ocprom.Options{
62 Registry: prometheus.DefaultRegisterer.(*prometheus.Registry),
63 OnError: func(err error) {
64 log.Errorw("OC default registry ERROR", "error", err)
65 },
66 })
67 if err != nil {
68 return nil, err
69 }
70
71 // register prometheus with opencensus
72 view.RegisterExporter(pe)
73
74 return mux, nil
75 }
76 }
77
78 // MetricsCollectionOption adds collection of net/http-related metrics.
79 func MetricsCollectionOption(handlerName string) ServeOption {
80 return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
81 // Adapted from github.com/prometheus/client_golang/prometheus/http.go
82 // Work around https://github.com/prometheus/client_golang/pull/311
83 opts := prometheus.SummaryOpts{
84 Namespace: "ipfs",
85 Subsystem: "http",
86 ConstLabels: prometheus.Labels{"handler": handlerName},
87 Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
88 }
89
90 // Legacy metric - new metrics are provided by boxo/gateway as gw_http_responses_total
91 reqCnt := prometheus.NewCounterVec(
92 prometheus.CounterOpts{
93 Namespace: opts.Namespace,
94 Subsystem: opts.Subsystem,
95 Name: "requests_total",
96 Help: "Total number of HTTP requests made.",
97 ConstLabels: opts.ConstLabels,
98 },
99 []string{"method", "code"},
100 )
101 if err := prometheus.Register(reqCnt); err != nil {
102 if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
103 reqCnt = are.ExistingCollector.(*prometheus.CounterVec)
104 } else {
105 return nil, err
106 }
107 }
108
109 opts.Name = "request_duration_seconds"
110 opts.Help = "The HTTP request latencies in seconds."
111 reqDur := prometheus.NewSummaryVec(opts, nil)
112 if err := prometheus.Register(reqDur); err != nil {
113 if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
114 reqDur = are.ExistingCollector.(*prometheus.SummaryVec)
115 } else {
116 return nil, err
117 }
118 }
119
120 opts.Name = "request_size_bytes"
121 opts.Help = "The HTTP request sizes in bytes."
122 reqSz := prometheus.NewSummaryVec(opts, nil)
123 if err := prometheus.Register(reqSz); err != nil {
124 if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
125 reqSz = are.ExistingCollector.(*prometheus.SummaryVec)
126 } else {
127 return nil, err
128 }
129 }
130
131 opts.Name = "response_size_bytes"
132 opts.Help = "The HTTP response sizes in bytes."
133 resSz := prometheus.NewSummaryVec(opts, nil)
134 if err := prometheus.Register(resSz); err != nil {
135 if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
136 resSz = are.ExistingCollector.(*prometheus.SummaryVec)
137 } else {
138 return nil, err
139 }
140 }
141
142 // Construct the mux
143 childMux := http.NewServeMux()
144 var promMux http.Handler = childMux
145 promMux = promhttp.InstrumentHandlerResponseSize(resSz, promMux)
146 promMux = promhttp.InstrumentHandlerRequestSize(reqSz, promMux)
147 promMux = promhttp.InstrumentHandlerDuration(reqDur, promMux)
148 promMux = promhttp.InstrumentHandlerCounter(reqCnt, promMux)
149 mux.Handle("/", promMux)
150
151 return childMux, nil
152 }
153 }
154
155 var peersTotalMetric = prometheus.NewDesc(
156 prometheus.BuildFQName("ipfs", "p2p", "peers_total"),
157 "Number of connected peers",
158 []string{"transport"},
159 nil,
160 )
161
162 type IpfsNodeCollector struct {
163 Node *core.IpfsNode
164 }
165
166 func (IpfsNodeCollector) Describe(ch chan<- *prometheus.Desc) {
167 ch <- peersTotalMetric
168 }
169
170 func (c IpfsNodeCollector) Collect(ch chan<- prometheus.Metric) {
171 for tr, val := range c.PeersTotalValues() {
172 ch <- prometheus.MustNewConstMetric(
173 peersTotalMetric,
174 prometheus.GaugeValue,
175 val,
176 tr,
177 )
178 }
179 }
180
181 func (c IpfsNodeCollector) PeersTotalValues() map[string]float64 {
182 vals := make(map[string]float64)
183 if c.Node.PeerHost == nil {
184 return vals
185 }
186 for _, peerID := range c.Node.PeerHost.Network().Peers() {
187 // Each peer may have more than one connection (see for an explanation
188 // https://github.com/libp2p/go-libp2p-swarm/commit/0538806), so we grab
189 // only one, the first (an arbitrary and non-deterministic choice), which
190 // according to ConnsToPeer is the oldest connection in the list
191 // (https://github.com/libp2p/go-libp2p-swarm/blob/v0.2.6/swarm.go#L362-L364).
192 conns := c.Node.PeerHost.Network().ConnsToPeer(peerID)
193 if len(conns) == 0 {
194 continue
195 }
196 tr := ""
197 for _, proto := range conns[0].RemoteMultiaddr().Protocols() {
198 tr = tr + "/" + proto.Name
199 }
200 vals[tr] = vals[tr] + 1
201 }
202 return vals
203 }