...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package githubapp
16
17 import (
18 "fmt"
19 "net/http"
20 "strconv"
21
22 "github.com/gregjones/httpcache"
23 "github.com/rcrowley/go-metrics"
24 )
25
26 const (
27 MetricsKeyRequests = "github.requests"
28 MetricsKeyRequests2xx = "github.requests.2xx"
29 MetricsKeyRequests3xx = "github.requests.3xx"
30 MetricsKeyRequests4xx = "github.requests.4xx"
31 MetricsKeyRequests5xx = "github.requests.5xx"
32
33 MetricsKeyRequestsCached = "github.requests.cached"
34
35 MetricsKeyRateLimit = "github.rate.limit"
36 MetricsKeyRateLimitRemaining = "github.rate.remaining"
37 )
38
39
40
41 func ClientMetrics(registry metrics.Registry) ClientMiddleware {
42 for _, key := range []string{
43 MetricsKeyRequests,
44 MetricsKeyRequests2xx,
45 MetricsKeyRequests3xx,
46 MetricsKeyRequests4xx,
47 MetricsKeyRequests5xx,
48 MetricsKeyRequestsCached,
49 } {
50
51
52 metrics.GetOrRegisterCounter(key, registry)
53 }
54
55 return func(next http.RoundTripper) http.RoundTripper {
56 return roundTripperFunc(func(r *http.Request) (*http.Response, error) {
57 installationID, ok := r.Context().Value(installationKey).(int64)
58 if !ok {
59 installationID = 0
60 }
61
62 res, err := next.RoundTrip(r)
63
64 if res != nil {
65 registry.Get(MetricsKeyRequests).(metrics.Counter).Inc(1)
66 if key := bucketStatus(res.StatusCode); key != "" {
67 registry.Get(key).(metrics.Counter).Inc(1)
68 }
69
70 if res.Header.Get(httpcache.XFromCache) != "" {
71 registry.Get(MetricsKeyRequestsCached).(metrics.Counter).Inc(1)
72 }
73
74 limitMetric := fmt.Sprintf("%s[installation:%d]", MetricsKeyRateLimit, installationID)
75 remainingMetric := fmt.Sprintf("%s[installation:%d]", MetricsKeyRateLimitRemaining, installationID)
76
77
78 updateRegistryForHeader(res.Header, "X-RateLimit-Limit", metrics.GetOrRegisterGauge(limitMetric, registry))
79 updateRegistryForHeader(res.Header, "X-RateLimit-Remaining", metrics.GetOrRegisterGauge(remainingMetric, registry))
80 }
81
82 return res, err
83 })
84 }
85 }
86
87 func updateRegistryForHeader(headers http.Header, header string, metric metrics.Gauge) {
88 headerString := headers.Get(header)
89 if headerString != "" {
90 headerVal, err := strconv.ParseInt(headerString, 10, 64)
91 if err == nil {
92 metric.Update(headerVal)
93 }
94 }
95 }
96
97 func bucketStatus(status int) string {
98 switch {
99 case status >= 200 && status < 300:
100 return MetricsKeyRequests2xx
101 case status >= 300 && status < 400:
102 return MetricsKeyRequests3xx
103 case status >= 400 && status < 500:
104 return MetricsKeyRequests4xx
105 case status >= 500 && status < 600:
106 return MetricsKeyRequests5xx
107 }
108 return ""
109 }
110
111 type roundTripperFunc func(*http.Request) (*http.Response, error)
112
113 func (fn roundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
114 return fn(r)
115 }
116
View as plain text