Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 13, 23:00 UTC
move common metrics-related funcs to internal package
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
Dave Henderson committed
Jan 24, 2022 at 08:35 UTC
7ca5921a87c819f9848ccd7ec786aab0f896be72
5 files changed
+74
-94
internal/metrics/metrics.go
new
+39
@@ -0,0 +1,39 @@
1
+package metrics
2
+
3
+import (
4
+ "net/http"
5
+ "strconv"
6
+)
7
+
8
+func SanitizeCode(s int) string {
9
+ switch s {
10
+ case 0, 200:
11
+ return "200"
12
+ default:
13
+ return strconv.Itoa(s)
14
+ }
15
+}
16
+
17
+// Only support the list of "regular" HTTP methods, see
18
+// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
19
+var methodMap = map[string]string{
20
+ "GET": http.MethodGet, "get": http.MethodGet,
21
+ "HEAD": http.MethodHead, "head": http.MethodHead,
22
+ "PUT": http.MethodPut, "put": http.MethodPut,
23
+ "POST": http.MethodPost, "post": http.MethodPost,
24
+ "DELETE": http.MethodDelete, "delete": http.MethodDelete,
25
+ "CONNECT": http.MethodConnect, "connect": http.MethodConnect,
26
+ "OPTIONS": http.MethodOptions, "options": http.MethodOptions,
27
+ "TRACE": http.MethodTrace, "trace": http.MethodTrace,
28
+ "PATCH": http.MethodPatch, "patch": http.MethodPatch,
29
+}
30
+
31
+// SanitizeMethod sanitizes the method for use as a metric label. This helps
32
+// prevent high cardinality on the method label. The name is always upper case.
33
+func SanitizeMethod(m string) string {
34
+ if m, ok := methodMap[m]; ok {
35
+ return m
36
+ }
37
+
38
+ return "OTHER"
39
+}
internal/metrics/metrics_test.go
new
+28
@@ -0,0 +1,28 @@
1
+package metrics
2
+
3
+import (
4
+ "strings"
5
+ "testing"
6
+)
7
+
8
+func TestSanitizeMethod(t *testing.T) {
9
+ tests := []struct {
10
+ method string
11
+ expected string
12
+ }{
13
+ {method: "get", expected: "GET"},
14
+ {method: "POST", expected: "POST"},
15
+ {method: "OPTIONS", expected: "OPTIONS"},
16
+ {method: "connect", expected: "CONNECT"},
17
+ {method: "trace", expected: "TRACE"},
18
+ {method: "UNKNOWN", expected: "OTHER"},
19
+ {method: strings.Repeat("ohno", 9999), expected: "OTHER"},
20
+ }
21
+
22
+ for _, d := range tests {
23
+ actual := SanitizeMethod(d.method)
24
+ if actual != d.expected {
25
+ t.Errorf("Not same: expected %#v, but got %#v", d.expected, actual)
26
+ }
27
+ }
28
+}
metrics.go
+3
-36
@@ -2,8 +2,8 @@ package caddy
2
3
import (
4
"net/http"
5
- "strconv"
5
6
+ "github.com/caddyserver/caddy/v2/internal/metrics"
7
"github.com/prometheus/client_golang/prometheus"
8
"github.com/prometheus/client_golang/prometheus/collectors"
9
"github.com/prometheus/client_golang/prometheus/promauto"
@@ -45,8 +45,8 @@ func instrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler)
45
d := newDelegator(w)
46
next.ServeHTTP(d, r)
47
counter.With(prometheus.Labels{
48
- "code": sanitizeCode(d.status),
49
- "method": sanitizeMethod(r.Method),
48
+ "code": metrics.SanitizeCode(d.status),
49
+ "method": metrics.SanitizeMethod(r.Method),
50
}).Inc()
51
})
52
}
@@ -66,36 +66,3 @@ func (d *delegator) WriteHeader(code int) {
66
d.status = code
67
d.ResponseWriter.WriteHeader(code)
68
}
69
-
70
-func sanitizeCode(s int) string {
71
- switch s {
72
- case 0, 200:
73
- return "200"
74
- default:
75
- return strconv.Itoa(s)
76
- }
77
-}
78
-
79
-// Only support the list of "regular" HTTP methods, see
80
-// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
81
-var methodMap = map[string]string{
82
- "GET": http.MethodGet, "get": http.MethodGet,
83
- "HEAD": http.MethodHead, "head": http.MethodHead,
84
- "PUT": http.MethodPut, "put": http.MethodPut,
85
- "POST": http.MethodPost, "post": http.MethodPost,
86
- "DELETE": http.MethodDelete, "delete": http.MethodDelete,
87
- "CONNECT": http.MethodConnect, "connect": http.MethodConnect,
88
- "OPTIONS": http.MethodOptions, "options": http.MethodOptions,
89
- "TRACE": http.MethodTrace, "trace": http.MethodTrace,
90
- "PATCH": http.MethodPatch, "patch": http.MethodPatch,
91
-}
92
-
93
-// sanitizeMethod sanitizes the method for use as a metric label. This helps
94
-// prevent high cardinality on the method label. The name is always upper case.
95
-func sanitizeMethod(m string) string {
96
- if m, ok := methodMap[m]; ok {
97
- return m
98
- }
99
-
100
- return "OTHER"
101
-}
modules/caddyhttp/metrics.go
+4
-35
@@ -3,10 +3,10 @@ package caddyhttp
3
import (
4
"context"
5
"net/http"
6
- "strconv"
6
"sync"
7
"time"
8
9
+ "github.com/caddyserver/caddy/v2/internal/metrics"
10
"github.com/prometheus/client_golang/prometheus"
11
"github.com/prometheus/client_golang/prometheus/promauto"
12
)
@@ -108,7 +108,7 @@ func newMetricsInstrumentedHandler(handler string, mh MiddlewareHandler) *metric
108
func (h *metricsInstrumentedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request, next Handler) error {
109
server := serverNameFromContext(r.Context())
110
labels := prometheus.Labels{"server": server, "handler": h.handler}
111
- method := sanitizeMethod(r.Method)
111
+ method := metrics.SanitizeMethod(r.Method)
112
// the "code" value is set later, but initialized here to eliminate the possibility
113
// of a panic
114
statusLabels := prometheus.Labels{"server": server, "handler": h.handler, "method": method, "code": ""}
@@ -123,7 +123,7 @@ func (h *metricsInstrumentedHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
123
// being called when the headers are written.
124
// Effectively the same behaviour as promhttp.InstrumentHandlerTimeToWriteHeader.
125
writeHeaderRecorder := ShouldBufferFunc(func(status int, header http.Header) bool {
126
- statusLabels["code"] = sanitizeCode(status)
126
+ statusLabels["code"] = metrics.SanitizeCode(status)
127
ttfb := time.Since(start).Seconds()
128
httpMetrics.responseDuration.With(statusLabels).Observe(ttfb)
129
return false
@@ -142,7 +142,7 @@ func (h *metricsInstrumentedHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
142
if statusLabels["code"] == "" {
143
// we still sanitize it, even though it's likely to be 0. A 200 is
144
// returned on fallthrough so we want to reflect that.
145
- statusLabels["code"] = sanitizeCode(wrec.Status())
145
+ statusLabels["code"] = metrics.SanitizeCode(wrec.Status())
146
}
147
148
httpMetrics.requestDuration.With(statusLabels).Observe(dur)
@@ -152,37 +152,6 @@ func (h *metricsInstrumentedHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
152
return nil
153
}
154
155
-func sanitizeCode(code int) string {
156
- if code == 0 {
157
- return "200"
158
- }
159
- return strconv.Itoa(code)
160
-}
161
-
162
-// Only support the list of "regular" HTTP methods, see
163
-// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
164
-var methodMap = map[string]string{
165
- "GET": http.MethodGet, "get": http.MethodGet,
166
- "HEAD": http.MethodHead, "head": http.MethodHead,
167
- "PUT": http.MethodPut, "put": http.MethodPut,
168
- "POST": http.MethodPost, "post": http.MethodPost,
169
- "DELETE": http.MethodDelete, "delete": http.MethodDelete,
170
- "CONNECT": http.MethodConnect, "connect": http.MethodConnect,
171
- "OPTIONS": http.MethodOptions, "options": http.MethodOptions,
172
- "TRACE": http.MethodTrace, "trace": http.MethodTrace,
173
- "PATCH": http.MethodPatch, "patch": http.MethodPatch,
174
-}
175
-
176
-// sanitizeMethod sanitizes the method for use as a metric label. This helps
177
-// prevent high cardinality on the method label. The name is always upper case.
178
-func sanitizeMethod(m string) string {
179
- if m, ok := methodMap[m]; ok {
180
- return m
181
- }
182
-
183
- return "OTHER"
184
-}
185
-
155
// taken from https://github.com/prometheus/client_golang/blob/6007b2b5cae01203111de55f753e76d8dac1f529/prometheus/promhttp/instrument_server.go#L298
156
func computeApproximateRequestSize(r *http.Request) int {
157
s := 0
modules/caddyhttp/metrics_test.go
-23
@@ -5,7 +5,6 @@ import (
5
"errors"
6
"net/http"
7
"net/http/httptest"
8
- "strings"
8
"testing"
9
10
"github.com/prometheus/client_golang/prometheus/testutil"
@@ -83,25 +82,3 @@ type middlewareHandlerFunc func(http.ResponseWriter, *http.Request, Handler) err
82
func (f middlewareHandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request, h Handler) error {
83
return f(w, r, h)
84
}
86
-
87
-func TestSanitizeMethod(t *testing.T) {
88
- tests := []struct {
89
- method string
90
- expected string
91
- }{
92
- {method: "get", expected: "GET"},
93
- {method: "POST", expected: "POST"},
94
- {method: "OPTIONS", expected: "OPTIONS"},
95
- {method: "connect", expected: "CONNECT"},
96
- {method: "trace", expected: "TRACE"},
97
- {method: "UNKNOWN", expected: "OTHER"},
98
- {method: strings.Repeat("ohno", 9999), expected: "OTHER"},
99
- }
100
-
101
- for _, d := range tests {
102
- actual := sanitizeMethod(d.method)
103
- if actual != d.expected {
104
- t.Errorf("Not same: expected %#v, but got %#v", d.expected, actual)
105
- }
106
- }
107
-}