diff --git a/middleware/metrics.go b/middleware/metrics.go index 1b6ebe3..b4f9bf2 100644 --- a/middleware/metrics.go +++ b/middleware/metrics.go @@ -1,6 +1,8 @@ package middleware import ( + "strconv" + "github.com/gin-gonic/gin" "github.com/prometheus/client_golang/prometheus" @@ -9,9 +11,17 @@ import ( const labelPath = "path" const labelMethod = "method" +const labelStatus = "status" + +const ( + _ = iota + _ + labelStatusIndex +) func MetricsMiddleware(namespace string, labels prometheus.Labels, reg prometheus.Registerer) gin.HandlerFunc { - perfMetric := metrics.NewHttpServerMetric(namespace, []string{labelPath, labelMethod}, labels, reg) + perfMetric := metrics.NewHttpServerMetric(namespace, []string{labelPath, labelMethod, labelStatus}, labels, reg) + return func(c *gin.Context) { path := c.FullPath() method := c.Request.Method @@ -22,13 +32,21 @@ func MetricsMiddleware(namespace string, labels prometheus.Labels, reg prometheu return } - labelValues := []string{path, method} + labelValues := []string{path, method, "none"} startTime := perfMetric.Start(labelValues...) + c.Next() + + var ( + statusCode = c.Writer.Status() + statusCodeStr = strconv.FormatInt(int64(statusCode), 10) + ) + labelValues[labelStatusIndex] = statusCodeStr + + // record duration with status code perfMetric.Duration(startTime, labelValues...) - statusCode := c.Writer.Status() switch { case 200 <= statusCode && statusCode <= 299: perfMetric.Success(labelValues...) diff --git a/middleware/metrics_test.go b/middleware/metrics_test.go index 3a4417c..409c56e 100644 --- a/middleware/metrics_test.go +++ b/middleware/metrics_test.go @@ -27,49 +27,68 @@ func TestMetricsMiddleware(t *testing.T) { router.GET("/error", func(c *gin.Context) { _ = c.AbortWithError(http.StatusInternalServerError, errors.New("oops error")) }) + router.GET("/404", func(c *gin.Context) { + _ = c.AbortWithError(http.StatusNotFound, errors.New("404")) + }) // 2 successes, 1 errors _ = performRequest("GET", "/success?haha=1&hoho=2", router) _ = performRequest("GET", "/error?hehe=1&huhu=3", router) _ = performRequest("GET", "/success/hihi", router) + _ = performRequest("GET", "/404", router) metricFamilies, err := r.Gather() require.NoError(t, err) - - const executionFailedTotal = "execution_failed_total" - const executionSucceededTotal = "execution_succeeded_total" - + const ( + requestSucceededTotalKey = "request_succeeded_total" + requestClientErrTotalKey = "request_client_error_total" + requestServerErrTotalKey = "request_server_error_total" + ) // metricFamily.Name --> label --> counter value expected := map[string]map[string]int{ - executionSucceededTotal: { + requestSucceededTotalKey: { "/success": 1, "/success/:test": 1, "/error": 0, + "/404": 0, }, - executionFailedTotal: { + requestServerErrTotalKey: { "/success": 0, "/success/:test": 0, "/error": 1, + "/404": 0, + }, + requestClientErrTotalKey: { + "/success": 0, + "/success/:test": 0, + "/error": 0, + "/404": 1, }, } - for _, metricFamily := range metricFamilies { expectedLabelCounterMap, ok := expected[*metricFamily.Name] if !ok { continue } - require.Len(t, metricFamily.Metric, len(expectedLabelCounterMap)) for _, metric := range metricFamily.Metric { - require.Len(t, metric.Label, 2) - var chosenLabelIdx = -1 + require.Len(t, metric.Label, 3) + labelIndexes := map[string]int{ + labelMethod: -1, + labelPath: -1, + labelStatus: -1, + } for idx, label := range metric.Label { - if *label.Name == labelPath { - chosenLabelIdx = idx - } + labelIndexes[*label.Name] = idx + } + require.Equal(t, len(labelIndexes), 3) + for _, labelIdx := range labelIndexes { + require.NotEqual(t, -1, labelIdx) } - require.NotEqual(t, -1, chosenLabelIdx) - require.Equal(t, float64(expectedLabelCounterMap[*metric.Label[chosenLabelIdx].Value]), *metric.Counter.Value) + pathIdx := labelIndexes[labelPath] + path := *metric.Label[pathIdx].Value + expectedPathMetric := float64(expectedLabelCounterMap[path]) + require.Equal(t, expectedPathMetric, *metric.Counter.Value) } } }