fork of indigo with slightly nicer lexgen
at main 4.2 kB view raw
1package search 2 3import ( 4 "errors" 5 "net/http" 6 "strconv" 7 "strings" 8 "time" 9 10 "github.com/labstack/echo/v4" 11 "github.com/prometheus/client_golang/prometheus" 12 "github.com/prometheus/client_golang/prometheus/promauto" 13) 14 15var postsReceived = promauto.NewCounter(prometheus.CounterOpts{ 16 Name: "search_posts_received", 17 Help: "Number of posts received", 18}) 19 20var postsIndexed = promauto.NewCounter(prometheus.CounterOpts{ 21 Name: "search_posts_indexed", 22 Help: "Number of posts indexed", 23}) 24 25var postsFailed = promauto.NewCounter(prometheus.CounterOpts{ 26 Name: "search_posts_failed", 27 Help: "Number of posts that failed indexing", 28}) 29 30var postsDeleted = promauto.NewCounter(prometheus.CounterOpts{ 31 Name: "search_posts_deleted", 32 Help: "Number of posts deleted", 33}) 34 35var profilesReceived = promauto.NewCounter(prometheus.CounterOpts{ 36 Name: "search_profiles_received", 37 Help: "Number of profiles received", 38}) 39 40var profilesIndexed = promauto.NewCounter(prometheus.CounterOpts{ 41 Name: "search_profiles_indexed", 42 Help: "Number of profiles indexed", 43}) 44 45var profilesFailed = promauto.NewCounter(prometheus.CounterOpts{ 46 Name: "search_profiles_failed", 47 Help: "Number of profiles that failed indexing", 48}) 49 50var profilesDeleted = promauto.NewCounter(prometheus.CounterOpts{ 51 Name: "search_profiles_deleted", 52 Help: "Number of profiles deleted", 53}) 54 55var currentSeq = promauto.NewGauge(prometheus.GaugeOpts{ 56 Name: "search_current_seq", 57 Help: "Current sequence number", 58}) 59 60var reqSz = promauto.NewHistogramVec(prometheus.HistogramOpts{ 61 Name: "http_request_size_bytes", 62 Help: "A histogram of request sizes for requests.", 63 Buckets: prometheus.ExponentialBuckets(100, 10, 8), 64}, []string{"code", "method", "path", "extras"}) 65 66var reqDur = promauto.NewHistogramVec(prometheus.HistogramOpts{ 67 Name: "http_request_duration_seconds", 68 Help: "A histogram of latencies for requests.", 69 Buckets: prometheus.ExponentialBuckets(0.0001, 2, 18), 70}, []string{"code", "method", "path", "extras"}) 71 72var reqCnt = promauto.NewCounterVec(prometheus.CounterOpts{ 73 Name: "http_requests_total", 74 Help: "A counter for requests to the wrapped handler.", 75}, []string{"code", "method", "path", "extras"}) 76 77var resSz = promauto.NewHistogramVec(prometheus.HistogramOpts{ 78 Name: "http_response_size_bytes", 79 Help: "A histogram of response sizes for requests.", 80 Buckets: prometheus.ExponentialBuckets(100, 10, 8), 81}, []string{"code", "method", "path", "extras"}) 82 83// MetricsMiddleware defines handler function for metrics middleware 84func MetricsMiddleware(next echo.HandlerFunc) echo.HandlerFunc { 85 return func(c echo.Context) error { 86 path := c.Path() 87 if path == "/metrics" || path == "/_health" { 88 return next(c) 89 } 90 91 start := time.Now() 92 requestSize := computeApproximateRequestSize(c.Request()) 93 94 err := next(c) 95 96 status := c.Response().Status 97 if err != nil { 98 var httpError *echo.HTTPError 99 if errors.As(err, &httpError) { 100 status = httpError.Code 101 } 102 if status == 0 || status == http.StatusOK { 103 status = http.StatusInternalServerError 104 } 105 } 106 107 elapsed := float64(time.Since(start)) / float64(time.Second) 108 109 statusStr := strconv.Itoa(status) 110 method := c.Request().Method 111 112 responseSize := float64(c.Response().Size) 113 114 // Custom label for Typeahead search queries 115 typeahead := false 116 if q := strings.TrimSpace(c.QueryParam("typeahead")); q == "true" || q == "1" || q == "y" { 117 typeahead = true 118 } 119 120 labels := []string{statusStr, method, path} 121 if typeahead { 122 labels = append(labels, "typeahead") 123 } else { 124 labels = append(labels, "_none") 125 } 126 127 reqDur.WithLabelValues(labels...).Observe(elapsed) 128 reqCnt.WithLabelValues(labels...).Inc() 129 reqSz.WithLabelValues(labels...).Observe(float64(requestSize)) 130 resSz.WithLabelValues(labels...).Observe(responseSize) 131 132 return err 133 } 134} 135 136func computeApproximateRequestSize(r *http.Request) int { 137 s := 0 138 if r.URL != nil { 139 s = len(r.URL.Path) 140 } 141 142 s += len(r.Method) 143 s += len(r.Proto) 144 for name, values := range r.Header { 145 s += len(name) 146 for _, value := range values { 147 s += len(value) 148 } 149 } 150 s += len(r.Host) 151 152 // N.B. r.Form and r.MultipartForm are assumed to be included in r.URL. 153 154 if r.ContentLength != -1 { 155 s += int(r.ContentLength) 156 } 157 return s 158}