diff --git a/echoprometheus/prometheus.go b/echoprometheus/prometheus.go index cc864c8..e8b93be 100644 --- a/echoprometheus/prometheus.go +++ b/echoprometheus/prometheus.go @@ -21,6 +21,7 @@ import ( "net/http" "sort" "strconv" + "strings" "time" ) @@ -271,15 +272,30 @@ func (conf MiddlewareConfig) ToMiddleware() (echo.MiddlewareFunc, error) { values[0] = strconv.Itoa(status) values[1] = c.Request().Method values[2] = c.Request().Host - values[3] = url + values[3] = strings.ToValidUTF8(url, "\uFFFD") // \uFFFD is � https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character for _, cv := range customValuers { values[cv.index] = cv.valueFunc(c, err) } - - requestDuration.WithLabelValues(values...).Observe(elapsed) - requestCount.WithLabelValues(values...).Inc() - requestSize.WithLabelValues(values...).Observe(float64(reqSz)) - responseSize.WithLabelValues(values...).Observe(float64(c.Response().Size)) + if obs, err := requestDuration.GetMetricWithLabelValues(values...); err == nil { + obs.Observe(elapsed) + } else { + return fmt.Errorf("failed to label request duration metric with values, err: %w", err) + } + if obs, err := requestCount.GetMetricWithLabelValues(values...); err == nil { + obs.Inc() + } else { + return fmt.Errorf("failed to label request count metric with values, err: %w", err) + } + if obs, err := requestSize.GetMetricWithLabelValues(values...); err == nil { + obs.Observe(float64(reqSz)) + } else { + return fmt.Errorf("failed to label request size metric with values, err: %w", err) + } + if obs, err := responseSize.GetMetricWithLabelValues(values...); err == nil { + obs.Observe(float64(c.Response().Size)) + } else { + return fmt.Errorf("failed to label response size metric with values, err: %w", err) + } return err } diff --git a/echoprometheus/prometheus_test.go b/echoprometheus/prometheus_test.go index 5418360..24ee2fa 100644 --- a/echoprometheus/prometheus_test.go +++ b/echoprometheus/prometheus_test.go @@ -316,6 +316,21 @@ func TestSetPathFor404Logic(t *testing.T) { unregisterDefaults(defaultSubsystem) } +func TestInvalidUTF8PathIsFixed(t *testing.T) { + e := echo.New() + + e.Use(NewMiddlewareWithConfig(MiddlewareConfig{Subsystem: defaultSubsystem})) + e.GET("/metrics", NewHandler()) + + assert.Equal(t, http.StatusNotFound, request(e, "/../../WEB-INF/web.xml\xc0\x80.jsp")) + + s, code := requestBody(e, "/metrics") + assert.Equal(t, http.StatusOK, code) + assert.Contains(t, s, fmt.Sprintf(`%s_request_duration_seconds_count{code="404",host="example.com",method="GET",url="/../../WEB-INF/web.xml�.jsp"} 1`, defaultSubsystem)) + + unregisterDefaults(defaultSubsystem) +} + func requestBody(e *echo.Echo, path string) (string, int) { req := httptest.NewRequest(http.MethodGet, path, nil) rec := httptest.NewRecorder() diff --git a/go.mod b/go.mod index 7302172..5fcdb9e 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/casbin/casbin/v2 v2.102.0 github.com/gorilla/context v1.1.2 github.com/gorilla/sessions v1.4.0 - github.com/labstack/echo/v4 v4.13.2 + github.com/labstack/echo/v4 v4.13.3 github.com/labstack/gommon v0.4.2 github.com/opentracing/opentracing-go v1.2.0 github.com/openzipkin/zipkin-go v0.4.3 @@ -44,6 +44,6 @@ require ( golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.8.0 // indirect google.golang.org/grpc v1.69.2 // indirect - google.golang.org/protobuf v1.36.0 // indirect + google.golang.org/protobuf v1.36.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 25ddcc5..2bcdef7 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +45,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/labstack/echo/v4 v4.13.2 h1:9aAt4hstpH54qIcqkuUXRLTf+v7yOTfMPWzDtuqLmtA= -github.com/labstack/echo/v4 v4.13.2/go.mod h1:uc9gDtHB8UWt3FfbYx0HyxcCuvR4YuPYOxF/1QjoV/c= +github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY= +github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -137,8 +137,8 @@ gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6d gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= -google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= -google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=