-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathserve_mux.go
164 lines (147 loc) · 4.47 KB
/
serve_mux.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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Package promhttp provides handy wrappers around http package objects that allow monitoring using prometheus.
package promhttp
import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
pph "github.com/prometheus/client_golang/prometheus/promhttp"
)
const (
subsystemHTTPOutgoing = "http_outgoing"
subsystemHTTPIncoming = "http_incoming"
)
type serveMux interface {
Handle(pattern string, handler http.Handler)
ServeHTTP(response http.ResponseWriter, request *http.Request)
}
// ServeMux is a wrapper for an http.ServeMux that provides Prometheus
// instrumentation for various metrics such as total requests.
type ServeMux struct {
ServeMux serveMux
metrics map[string]*incomingInstrumentation
Namespace string
}
// Handle wraps a normal http.Handle function and instruments it
// for Prometheus.
func (sm *ServeMux) Handle(path string, h http.Handler) {
if sm.metrics == nil {
sm.metrics = make(map[string]*incomingInstrumentation)
}
constLabels := map[string]string{
"path": path,
}
commonLabels := []string{"code", "method"}
ins := &incomingInstrumentation{
duration: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: sm.Namespace,
Subsystem: subsystemHTTPIncoming,
Name: "request_duration_histogram_seconds",
Help: "Request time duration.",
Buckets: prometheus.DefBuckets,
ConstLabels: constLabels,
},
commonLabels,
),
requests: prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: sm.Namespace,
Subsystem: subsystemHTTPIncoming,
Name: "requests_total",
Help: "Total number of requests received.",
ConstLabels: constLabels,
},
commonLabels,
),
requestSize: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: sm.Namespace,
Subsystem: subsystemHTTPIncoming,
Name: "request_size_histogram_bytes",
Help: "Request size in bytes.",
Buckets: []float64{100, 1000, 2000, 5000, 10000},
ConstLabels: constLabels,
},
commonLabels,
),
responseSize: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: sm.Namespace,
Subsystem: subsystemHTTPIncoming,
Name: "response_size_histogram_bytes",
Help: "Response size in bytes.",
Buckets: []float64{100, 1000, 2000, 5000, 10000},
ConstLabels: constLabels,
},
commonLabels,
),
inflight: prometheus.NewGauge(
prometheus.GaugeOpts{
Namespace: sm.Namespace,
Subsystem: subsystemHTTPIncoming,
Name: "in_flight_requests",
Help: "Number of http requests which are currently running.",
ConstLabels: constLabels,
},
),
}
chain := pph.InstrumentHandlerDuration(
ins.duration,
pph.InstrumentHandlerCounter(
ins.requests,
pph.InstrumentHandlerRequestSize(
ins.requestSize,
pph.InstrumentHandlerResponseSize(
ins.responseSize,
pph.InstrumentHandlerInFlight(
ins.inflight,
h,
),
),
)),
)
sm.ServeMux.Handle(path, http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
chain.ServeHTTP(rw, r)
}))
sm.metrics[path] = ins
}
// HandleFunc registers the handler function for the given pattern. It uses instrumented Handler implementation.
func (sm *ServeMux) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
sm.Handle(pattern, http.HandlerFunc(handler))
}
// ServeHTTP implements http Handler interface.
func (sm *ServeMux) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
sm.ServeMux.ServeHTTP(rw, r)
}
// Describe implements prometheus.Collector interface.
func (sm *ServeMux) Describe(in chan<- *prometheus.Desc) {
for _, col := range sm.metrics {
col.describe(in)
}
}
// Collect implements prometheus.Collector interface.
func (sm *ServeMux) Collect(in chan<- prometheus.Metric) {
for _, col := range sm.metrics {
col.collect(in)
}
}
type incomingInstrumentation struct {
duration *prometheus.HistogramVec
requests *prometheus.CounterVec
requestSize *prometheus.HistogramVec
responseSize *prometheus.HistogramVec
inflight prometheus.Gauge
}
func (i *incomingInstrumentation) describe(in chan<- *prometheus.Desc) {
i.duration.Describe(in)
i.requests.Describe(in)
i.requestSize.Describe(in)
i.responseSize.Describe(in)
i.inflight.Describe(in)
}
func (i *incomingInstrumentation) collect(in chan<- prometheus.Metric) {
i.duration.Collect(in)
i.requests.Collect(in)
i.requestSize.Collect(in)
i.responseSize.Collect(in)
i.inflight.Collect(in)
}