-
Notifications
You must be signed in to change notification settings - Fork 1
/
prometheus.go
111 lines (87 loc) · 2.56 KB
/
prometheus.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package middleware
import (
"context"
"errors"
"net/http"
"strconv"
"time"
"github.com/go-resty/resty/v2"
prom "github.com/prometheus/client_golang/prometheus"
)
const defaultSubsystem = "resty"
type ctxkey struct{ name string }
var urlKey = &ctxkey{"request-url"}
type prometheus struct {
reqTotal *prom.CounterVec
reqDur *prom.HistogramVec
}
func (p *prometheus) register(reg prom.Registerer, subsystem string) {
if subsystem == "" {
subsystem = defaultSubsystem
}
p.reqTotal = prom.NewCounterVec(prom.CounterOpts{
Name: "requests_total",
Subsystem: subsystem,
Help: "The number of requests made",
}, []string{"code", "method", "host", "url"})
p.reqDur = prom.NewHistogramVec(prom.HistogramOpts{
Name: "request_duration_seconds",
Subsystem: subsystem,
Help: "The request latency in seconds",
}, []string{"code", "method", "host", "url"})
reg.MustRegister(p.reqTotal)
reg.MustRegister(p.reqDur)
}
func (p *prometheus) collect(req *http.Request, code int, dur time.Duration) {
url, _ := req.Context().Value(urlKey).(string)
values := []string{
strconv.Itoa(code),
req.Method,
req.URL.Hostname(),
url,
}
p.reqTotal.WithLabelValues(values...).Inc()
p.reqDur.WithLabelValues(values...).Observe(dur.Seconds())
}
func (p *prometheus) beforeRequest(client *resty.Client, req *resty.Request) error {
ctx := context.WithValue(req.Context(), urlKey, req.URL)
req.SetContext(ctx)
return nil
}
func (p *prometheus) collectAfterResponse(client *resty.Client, res *resty.Response) error {
p.collect(
res.Request.RawRequest,
res.StatusCode(),
res.Time(),
)
return nil
}
func (p *prometheus) collectError(req *resty.Request, err error) {
code := http.StatusInternalServerError
var dur time.Duration
var e *resty.ResponseError
if errors.As(err, &e) {
code = e.Response.StatusCode()
dur = e.Response.Time()
}
p.collect(
req.RawRequest,
code,
dur,
)
}
// Prometheus generates a new set of metrics with a certain subsystem name from
// resty requests.
func Prometheus(client *resty.Client, subsystem string) *resty.Client {
return PrometheusWithRegister(client, prom.DefaultRegisterer, subsystem)
}
// PrometheusWithRegister generates a new set of metrics with a certain
// subsystem name from resty requests with a custom prometheus registerer.
func PrometheusWithRegister(client *resty.Client, reg prom.Registerer, subsystem string) *resty.Client {
p := &prometheus{}
p.register(reg, subsystem)
return client.
OnBeforeRequest(p.beforeRequest).
OnAfterResponse(p.collectAfterResponse).
OnError(p.collectError)
}