/
metrics.go
93 lines (78 loc) · 2.79 KB
/
metrics.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
package middleware
import (
"net/http"
"time"
"github.com/prometheus/client_golang/prometheus"
)
type prometheusHandler struct {
handler http.Handler
requests *prometheus.CounterVec
duration *prometheus.HistogramVec
responseSize *prometheus.HistogramVec
}
// NewMetricsHandler returns middleware that collects HTTP req/resp specific metrics
func NewMetricsHandler(svcName string, handler http.Handler) http.Handler {
requests := prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Number of processed HTTP requests labeled with status code, method and HTTP path.",
ConstLabels: prometheus.Labels{"service": svcName},
},
[]string{"code", "method", "path"},
)
prometheus.MustRegister(requests)
duration := prometheus.NewHistogramVec(prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "Duration of the HTTP request processing labeled with status code, method and HTTP path.",
ConstLabels: prometheus.Labels{"service": svcName},
Buckets: []float64{.01, .05, .1, .5, 1, 2.5, 5, 10, 20, 30, 50},
},
[]string{"code", "method", "path"},
)
prometheus.MustRegister(duration)
responseSize := prometheus.NewHistogramVec(prometheus.HistogramOpts{
Name: "http_response_size_bytes",
Help: "Size of the HTTP response labeled with status code, method and HTTP path.",
ConstLabels: prometheus.Labels{"service": svcName},
Buckets: prometheus.ExponentialBuckets(100, 10, 5),
},
[]string{"code", "method", "path"},
)
prometheus.MustRegister(responseSize)
return &prometheusHandler{
handler: handler,
requests: requests,
duration: duration,
responseSize: responseSize,
}
}
func (h *prometheusHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
start := time.Now()
infoWriter := wrapResponseWrite(writer)
defer func() {
h.requests.WithLabelValues(http.StatusText(infoWriter.status), request.Method, request.URL.Path).Inc()
h.duration.WithLabelValues(http.StatusText(infoWriter.status), request.Method, request.URL.Path).Observe(time.Since(start).Seconds())
h.responseSize.WithLabelValues(http.StatusText(infoWriter.status), request.Method, request.URL.Path).Observe(float64(infoWriter.size))
}()
h.handler.ServeHTTP(infoWriter, request)
}
type infoResponseWriter struct {
http.ResponseWriter
status int
size int
}
func wrapResponseWrite(writer http.ResponseWriter) *infoResponseWriter {
return &infoResponseWriter{
ResponseWriter: writer,
status: http.StatusOK,
}
}
func (w *infoResponseWriter) Write(data []byte) (int, error) {
size, err := w.ResponseWriter.Write(data)
w.size += size
return size, err
}
func (w *infoResponseWriter) WriteHeader(status int) {
w.ResponseWriter.WriteHeader(status)
w.status = status
}