Skip to content

Commit 016a7e5

Browse files
committed
bitswap/httpnet: collect metrics
1 parent 54b1dbe commit 016a7e5

3 files changed

Lines changed: 147 additions & 0 deletions

File tree

bitswap/network/httpnet/httpnet.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ type httpnet struct {
129129
supportsHave bool
130130
insecureSkipVerify bool
131131
allowlist map[string]struct{}
132+
133+
metrics *metrics
132134
}
133135

134136
// New returns a BitSwapNetwork supported by underlying IPFS host.
@@ -143,6 +145,7 @@ func New(host host.Host, opts ...Option) *httpnet {
143145
maxIdleConns: DefaultMaxIdleConns,
144146
supportsHave: DefaultSupportsHave,
145147
insecureSkipVerify: DefaultInsecureSkipVerify,
148+
metrics: newMetrics(),
146149
}
147150

148151
for _, opt := range opts {

bitswap/network/httpnet/metrics.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package httpnet
2+
3+
import (
4+
"context"
5+
6+
imetrics "github.com/ipfs/go-metrics-interface"
7+
)
8+
9+
var durationHistogramBuckets = []float64{0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 30, 60, 120, 240, 480, 960, 1920}
10+
11+
type ctxKeyT string
12+
13+
var ctxKey ctxKeyT = ctxKeyT(imetrics.CtxScopeKey)
14+
15+
func requestsInFlight(ctx context.Context) imetrics.Gauge {
16+
return imetrics.NewCtx(ctx, "httpnet_requests_in_flight", "Current number of in-flight requests").Gauge()
17+
}
18+
19+
func requestsTotal(ctx context.Context) imetrics.Counter {
20+
return imetrics.NewCtx(ctx, "httpnet_requests_total", "Total request count").Counter()
21+
}
22+
23+
func requestsFailure(ctx context.Context) imetrics.Counter {
24+
return imetrics.NewCtx(ctx, "httpnet_requests_failure", "Failed (no response, dial error etc) requests count").Counter()
25+
}
26+
27+
func requestsBodyFailure(ctx context.Context) imetrics.Counter {
28+
return imetrics.NewCtx(ctx, "httpnet_requests_body_failure", "Failure count when reading response body").Counter()
29+
}
30+
31+
func statusNotFound(ctx context.Context) imetrics.Counter {
32+
return imetrics.NewCtx(ctx, "httpnet_status_404", "Request count with NotFound status").Counter()
33+
}
34+
35+
func statusGone(ctx context.Context) imetrics.Counter {
36+
return imetrics.NewCtx(ctx, "httpnet_status_410", "Request count with Gone status").Counter()
37+
}
38+
39+
func statusForbidden(ctx context.Context) imetrics.Counter {
40+
return imetrics.NewCtx(ctx, "httpnet_status_403", "Request count with Forbidden status").Counter()
41+
}
42+
43+
func statusUnavailableForLegalReasons(ctx context.Context) imetrics.Counter {
44+
return imetrics.NewCtx(ctx, "httpnet_status_451", "Request count with Unavailable For Legal Reasons status").Counter()
45+
}
46+
47+
func statusOK(ctx context.Context) imetrics.Counter {
48+
return imetrics.NewCtx(ctx, "httpnet_status_200", "Request count with OK status").Counter()
49+
}
50+
51+
func statusTooManyRequests(ctx context.Context) imetrics.Counter {
52+
return imetrics.NewCtx(ctx, "httpnet_status_429", "Request count with Too Many Requests status").Counter()
53+
}
54+
55+
func statusServiceUnavailable(ctx context.Context) imetrics.Counter {
56+
return imetrics.NewCtx(ctx, "httpnet_status_503", "Request count with Service Unavailable status").Counter()
57+
}
58+
59+
func statusInternalServerError(ctx context.Context) imetrics.Counter {
60+
return imetrics.NewCtx(ctx, "httpnet_status_500", "Request count with Internal Server Error status").Counter()
61+
}
62+
63+
func statusOthers(ctx context.Context) imetrics.Counter {
64+
return imetrics.NewCtx(ctx, "httpnet_status_others", "Request count with other status codes").Counter()
65+
}
66+
67+
func requestTime(ctx context.Context) imetrics.Histogram {
68+
return imetrics.NewCtx(ctx, "httpnet_request_duration_seconds", "Histogram of request durations").Histogram(durationHistogramBuckets)
69+
}
70+
71+
type metrics struct {
72+
RequestsInFlight imetrics.Gauge
73+
RequestsTotal imetrics.Counter
74+
RequestsFailure imetrics.Counter
75+
RequestsBodyFailure imetrics.Counter
76+
StatusNotFound imetrics.Counter
77+
StatusGone imetrics.Counter
78+
StatusForbidden imetrics.Counter
79+
StatusUnavailableForLegalReasons imetrics.Counter
80+
StatusOK imetrics.Counter
81+
StatusTooManyRequests imetrics.Counter
82+
StatusServiceUnavailable imetrics.Counter
83+
StatusInternalServerError imetrics.Counter
84+
StatusOthers imetrics.Counter
85+
RequestTime imetrics.Histogram
86+
}
87+
88+
func newMetrics() *metrics {
89+
ctx := context.WithValue(context.Background(), ctxKey, "exchange")
90+
91+
return &metrics{
92+
RequestsInFlight: requestsInFlight(ctx),
93+
RequestsTotal: requestsTotal(ctx),
94+
RequestsFailure: requestsFailure(ctx),
95+
RequestsBodyFailure: requestsBodyFailure(ctx),
96+
StatusNotFound: statusNotFound(ctx),
97+
StatusGone: statusGone(ctx),
98+
StatusForbidden: statusForbidden(ctx),
99+
StatusUnavailableForLegalReasons: statusUnavailableForLegalReasons(ctx),
100+
StatusOK: statusOK(ctx),
101+
StatusTooManyRequests: statusTooManyRequests(ctx),
102+
StatusServiceUnavailable: statusServiceUnavailable(ctx),
103+
StatusInternalServerError: statusInternalServerError(ctx),
104+
StatusOthers: statusOthers(ctx),
105+
RequestTime: requestTime(ctx),
106+
}
107+
}
108+
109+
func (m *metrics) updateStatusCounter(statusCode int) {
110+
m.RequestsTotal.Inc()
111+
switch statusCode {
112+
case 404:
113+
m.StatusNotFound.Inc()
114+
case 410:
115+
m.StatusGone.Inc()
116+
case 403:
117+
m.StatusForbidden.Inc()
118+
case 451:
119+
m.StatusUnavailableForLegalReasons.Inc()
120+
case 200:
121+
m.StatusOK.Inc()
122+
case 429:
123+
m.StatusTooManyRequests.Inc()
124+
case 503:
125+
m.StatusServiceUnavailable.Inc()
126+
case 500:
127+
m.StatusInternalServerError.Inc()
128+
default:
129+
m.StatusOthers.Inc()
130+
}
131+
}

bitswap/network/httpnet/msg_sender.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,13 @@ func (sender *httpMsgSender) tryURL(ctx context.Context, u *senderURL, entry bsm
207207

208208
log.Debugf("%s %q", method, req.URL)
209209
atomic.AddUint64(&sender.ht.stats.MessagesSent, 1)
210+
reqStart := time.Now()
211+
sender.ht.metrics.RequestsInFlight.Inc()
210212
resp, err := sender.ht.client.Do(req)
211213
if err != nil {
212214
err = fmt.Errorf("error making request to %q: %w", req.URL, err)
215+
sender.ht.metrics.RequestsFailure.Inc()
216+
sender.ht.metrics.RequestsInFlight.Dec()
213217
log.Debug(err)
214218
// Something prevents us from making a request. We cannot
215219
// dial, or setup the connection perhaps. This counts as
@@ -241,13 +245,22 @@ func (sender *httpMsgSender) tryURL(ctx context.Context, u *senderURL, entry bsm
241245
if err != nil {
242246
// treat this as server error
243247
err = fmt.Errorf("error reading body from %q: %w", req.URL, err)
248+
sender.ht.metrics.RequestsBodyFailure.Inc()
249+
sender.ht.metrics.RequestsInFlight.Dec()
244250
log.Debug(err)
245251
return &senderError{
246252
Type: typeServer,
247253
Err: err,
248254
}
249255
}
256+
reqDuration := time.Since(reqStart)
257+
258+
sender.ht.metrics.RequestsInFlight.Dec()
259+
sender.ht.metrics.RequestTime.Observe(float64(reqDuration) / float64(time.Second))
260+
sender.ht.metrics.updateStatusCounter(resp.StatusCode)
261+
250262
sender.ht.connEvtMgr.OnMessage(sender.peer)
263+
251264
switch resp.StatusCode {
252265
// Valid responses signaling unavailability of the
253266
// content.

0 commit comments

Comments
 (0)