From 4bff6d85a46fd18c210f6a7d4a60136eda43bc24 Mon Sep 17 00:00:00 2001 From: Martin Polden Date: Wed, 24 Jun 2020 18:58:36 +0200 Subject: http: Add runtime metrics --- http/http.go | 35 +++++++++-------------------------- http/http_test.go | 16 +++++++++------- http/prometheus.go | 19 +++++++++++++++++++ 3 files changed, 37 insertions(+), 33 deletions(-) create mode 100644 http/prometheus.go (limited to 'http') diff --git a/http/http.go b/http/http.go index e783319..5ff5fd3 100644 --- a/http/http.go +++ b/http/http.go @@ -1,11 +1,9 @@ package http import ( - "bytes" "context" "encoding/json" "fmt" - "io" "log" "net" "net/http" @@ -22,7 +20,7 @@ const ( jsonMediaType = "application/json" ) -// A Server defines paramaters for running an HTTP server. The HTTP server serves an API for inspecting cache contents +// A Server defines parameters for running an HTTP server. The HTTP server serves an API for inspecting cache contents // and request log. type Server struct { cache *cache.Cache @@ -99,10 +97,10 @@ func newHTTPBadRequest(err error) *httpError { func NewServer(cache *cache.Cache, logger *sql.Logger, sqlCache *sql.Cache, addr string) *Server { server := &http.Server{Addr: addr} s := &Server{ + server: server, cache: cache, logger: logger, sqlCache: sqlCache, - server: server, } s.server.Handler = s.handler() return s @@ -111,9 +109,11 @@ func NewServer(cache *cache.Cache, logger *sql.Logger, sqlCache *sql.Cache, addr func (s *Server) handler() http.Handler { r := &router{} r.route(http.MethodGet, "/cache/v1/", s.cacheHandler) - r.route(http.MethodGet, "/log/v1/", s.logHandler) - r.route(http.MethodGet, "/metric/v1/", s.metricHandler) r.route(http.MethodDelete, "/cache/v1/", s.cacheResetHandler) + if s.logger != nil { + r.route(http.MethodGet, "/log/v1/", s.logHandler) + r.route(http.MethodGet, "/metric/v1/", s.metricHandler) + } return r.handler() } @@ -148,21 +148,6 @@ func writeJSON(w http.ResponseWriter, data interface{}) { w.Write(b) } -func writeMetric(w io.StringWriter, name, help string, value int64) { - w.WriteString("# HELP ") - w.WriteString(name) - w.WriteString(" ") - w.WriteString(help) - w.WriteString("\n") - w.WriteString("# TYPE ") - w.WriteString(name) - w.WriteString(" gauge\n") - w.WriteString(name) - w.WriteString(" ") - w.WriteString(strconv.FormatInt(value, 10)) - w.WriteString("\n") -} - func (s *Server) cacheHandler(w http.ResponseWriter, r *http.Request) *httpError { count, err := countFrom(r) if err != nil { @@ -268,11 +253,9 @@ func (s *Server) prometheusMetricHandler(w http.ResponseWriter, r *http.Request) if err != nil { return newHTTPError(err) } - var buf bytes.Buffer - writeMetric(&buf, "zdns_requests_total", "The total number of DNS requests.", lstats.Total) - writeMetric(&buf, "zdns_requests_hijacked", "The number of hijacked DNS requests.", lstats.Hijacked) - w.Header().Set("Content-Type", "text/plain; version=0.0.4") - w.Write(buf.Bytes()) + totalRequestsGauge.Set(float64(lstats.Total)) + hijackedRequestsGauge.Set(float64(lstats.Hijacked)) + prometheusHandler.ServeHTTP(w, r) return nil } diff --git a/http/http_test.go b/http/http_test.go index 905e338..ac95697 100644 --- a/http/http_test.go +++ b/http/http_test.go @@ -37,8 +37,8 @@ func testServer() (*httptest.Server, *Server) { logger := sql.NewLogger(sqlClient, sql.LogAll, 0) sqlCache := sql.NewCache(sqlClient) cache := cache.New(10, nil) - server := Server{logger: logger, cache: cache, sqlCache: sqlCache} - return httptest.NewServer(server.handler()), &server + server := NewServer(cache, logger, sqlCache, "") + return httptest.NewServer(server.handler()), server } func httpGet(url string) (*http.Response, string, error) { @@ -91,14 +91,15 @@ func TestRequests(t *testing.T) { `{"time":"RFC3339","remote_addr":"127.0.0.42","hijacked":false,"type":"A","question":"example.com.","answers":["192.0.2.101","192.0.2.100"]}]` lr2 := `[{"time":"RFC3339","remote_addr":"127.0.0.254","hijacked":true,"type":"AAAA","question":"example.com.","answers":["2001:db8::1"]}]` mr1 := `{"summary":{"log":{"since":"RFC3339","total":2,"hijacked":1,"pending_tasks":0},"cache":{"size":2,"capacity":10,"pending_tasks":0,"backend":{"pending_tasks":0}}},"requests":[{"time":"RFC3339","count":2}]}` - mr2 := `# HELP zdns_requests_total The total number of DNS requests. -# TYPE zdns_requests_total gauge -zdns_requests_total 2 + mr2 := ` + # HELP zdns_requests_hijacked The number of hijacked DNS requests. # TYPE zdns_requests_hijacked gauge zdns_requests_hijacked 1 +# HELP zdns_requests_total The total number of DNS requests. +# TYPE zdns_requests_total gauge +zdns_requests_total 2 ` - var tests = []struct { method string url string @@ -115,7 +116,7 @@ zdns_requests_hijacked 1 {http.MethodGet, "/cache/v1/?n=1", cr2, 200, jsonMediaType}, {http.MethodGet, "/metric/v1/", mr1, 200, jsonMediaType}, {http.MethodGet, "/metric/v1/?format=basic", mr1, 200, jsonMediaType}, - {http.MethodGet, "/metric/v1/?format=prometheus", mr2, 200, "text/plain; version=0.0.4"}, + {http.MethodGet, "/metric/v1/?format=prometheus", mr2, 200, "text/plain; version=0.0.4; charset=utf-8"}, {http.MethodGet, "/metric/v1/?resolution=1m", mr1, 200, jsonMediaType}, {http.MethodGet, "/metric/v1/?resolution=0", mr1, 200, jsonMediaType}, {http.MethodGet, "/metric/v1/?format=foo", `{"status":400,"message":"invalid metric format: foo"}`, 400, jsonMediaType}, @@ -151,6 +152,7 @@ zdns_requests_hijacked 1 got := string(data) want := regexp.QuoteMeta(tt.response) want = strings.ReplaceAll(want, "RFC3339", `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`) + want = strings.ReplaceAll(want, "", ".*") matched, err := regexp.MatchString(want, got) if err != nil { t.Fatal(err) diff --git a/http/prometheus.go b/http/prometheus.go new file mode 100644 index 0000000..7b78055 --- /dev/null +++ b/http/prometheus.go @@ -0,0 +1,19 @@ +package http + +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +var ( + totalRequestsGauge = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "zdns_requests_total", + Help: "The total number of DNS requests.", + }) + hijackedRequestsGauge = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "zdns_requests_hijacked", + Help: "The number of hijacked DNS requests.", + }) + prometheusHandler = promhttp.Handler() +) -- cgit v1.2.3