@marcel91200 / caddy Mirror · read-only / commits / 7ca5921a
Fork 0

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 -}