From b29ad0afa15f8f34f9825cadc31ee97559ebcfd7 Mon Sep 17 00:00:00 2001 From: "Gabriel A. Giovanini" Date: Fri, 22 Mar 2024 18:28:31 +0100 Subject: feat: Add metrics --- main.go | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 15 deletions(-) (limited to 'main.go') diff --git a/main.go b/main.go index 945b30b..b9bed96 100644 --- a/main.go +++ b/main.go @@ -8,28 +8,52 @@ import ( "io" "net/http" "regexp" + "strconv" "strings" + "time" "github.com/beevik/etree" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" ) type ErrorRequestHandler func(w http.ResponseWriter, r *http.Request) error -var RegexCollection = map[string]string{ - "nerdcast": "NerdCast [0-9]+[a-c]* -", - "empreendedor": "Empreendedor [0-9]+ -", - "mamicas": "Caneca de Mamicas [0-9]+ -", - "english": "Speak English [0-9]+ -", - "nerdcash": "NerdCash [0-9]+ -", - "bunker": "Lá do Bunker [0-9]+ -", - "tech": "NerdTech [0-9]+ -", - "genera": "Generacast [0-9]+ -", -} - const ( FeedUrl = "https://api.jovemnerd.com.br/feed-nerdcast/" ) +var ( + RegexCollection = map[string]string{ + "nerdcast": "NerdCast [0-9]+[a-c]* -", + "empreendedor": "Empreendedor [0-9]+ -", + "mamicas": "Caneca de Mamicas [0-9]+ -", + "english": "Speak English [0-9]+ -", + "nerdcash": "NerdCash [0-9]+ -", + "bunker": "Lá do Bunker [0-9]+ -", + "tech": "NerdTech [0-9]+ -", + "genera": "Generacast [0-9]+ -", + } + + feedRequest = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "feed_request", + Help: "How long jovemnerd takes to answer", + Buckets: []float64{.01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}, + }, []string{"status_code"}) + + httpRequest = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "http_request", + Help: "How long the application takes to complete the request", + Buckets: []float64{.01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}, + }, []string{"status_code", "user_agent"}) + + seriesCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "serie_count", + Help: "How often a serie is called", + }, []string{"serie"}) +) + func getSeries(r *http.Request) []string { query := r.URL.Query().Get("q") @@ -59,13 +83,24 @@ func match(title string, series []string) bool { } func fetchXML(_ context.Context) ([]byte, error) { + t := time.Now() + c := http.StatusInternalServerError + + defer func() { + since := time.Since(t).Seconds() + code := strconv.Itoa(c) + feedRequest.WithLabelValues(code).Observe(since) + }() + res, err := http.Get(FeedUrl) if err != nil { return nil, err } defer res.Body.Close() - if res.StatusCode == http.StatusOK { + c = res.StatusCode + + if c == http.StatusOK { return io.ReadAll(res.Body) } @@ -109,7 +144,7 @@ func filterBySeries(series []string, xml []byte, temper bool) ([]byte, error) { return doc.WriteToBytes() } -func wrap(next ErrorRequestHandler) http.HandlerFunc { +func handleError(next ErrorRequestHandler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if err := next(w, r); err != nil { w.WriteHeader(http.StatusInternalServerError) @@ -117,6 +152,30 @@ func wrap(next ErrorRequestHandler) http.HandlerFunc { } } +func observe(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + t := time.Now() + + next(w, r) + + rw := w.(*responseWriter) + since := time.Since(t).Seconds() + code := strconv.Itoa(rw.Status()) + userAgent := r.Header.Get("user-agent") + httpRequest.WithLabelValues(code, userAgent).Observe(float64(since)) + + for _, s := range getSeries(r) { + seriesCount.WithLabelValues(s).Inc() + } + } +} + +func wrap(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + next(NewResponseWriter(w), r) + } +} + func titles(w http.ResponseWriter, r *http.Request) error { xml, err := fetchXML(r.Context()) if err != nil { @@ -173,8 +232,9 @@ func main() { flag.Parse() mux := http.NewServeMux() - mux.HandleFunc("/titles", wrap(titles)) - mux.HandleFunc("/", wrap(podcast)) + mux.Handle("/metrics", promhttp.Handler()) + mux.HandleFunc("/titles", wrap(handleError(titles))) + mux.HandleFunc("/", wrap(observe(handleError(podcast)))) server := http.Server{ Handler: mux, -- cgit v1.2.3