Skip to content

Commit

Permalink
Conditionally set url label for 404 responses (#111)
Browse files Browse the repository at this point in the history
* conditionally set url label for 404 responses
  • Loading branch information
therealak12 authored Mar 21, 2024
1 parent fdc9c9a commit 7605e2a
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 1 deletion.
6 changes: 5 additions & 1 deletion echoprometheus/prometheus.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ type MiddlewareConfig struct {
AfterNext func(c echo.Context, err error)

timeNow func() time.Time

// If DoNotUseRequestPathFor404 is true, all 404 responses (due to non-matching route) will have the same `url` label and
// thus won't generate new metrics.
DoNotUseRequestPathFor404 bool
}

type LabelValueFunc func(c echo.Context, err error) string
Expand Down Expand Up @@ -246,7 +250,7 @@ func (conf MiddlewareConfig) ToMiddleware() (echo.MiddlewareFunc, error) {
}

url := c.Path() // contains route path ala `/users/:id`
if url == "" {
if url == "" && !conf.DoNotUseRequestPathFor404 {
// as of Echo v4.10.1 path is empty for 404 cases (when router did not find any matching routes)
// in this case we use actual path from request to have some distinction in Prometheus
url = c.Request().URL.Path
Expand Down
39 changes: 39 additions & 0 deletions echoprometheus/prometheus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,45 @@ func TestRunPushGatewayGatherer(t *testing.T) {

assert.EqualError(t, err, "code=400, message=post metrics request did not succeed")
assert.True(t, receivedMetrics)
unregisterDefaults("myapp")
}

// TestSetPathFor404NoMatchingRoute tests that the url is not included in the metric when
// the 404 response is due to no matching route
func TestSetPathFor404NoMatchingRoute(t *testing.T) {
e := echo.New()

e.Use(NewMiddlewareWithConfig(MiddlewareConfig{DoNotUseRequestPathFor404: true, Subsystem: defaultSubsystem}))
e.GET("/metrics", NewHandler())

assert.Equal(t, http.StatusNotFound, request(e, "/nonExistentPath"))

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=""} 1`, defaultSubsystem))
assert.NotContains(t, s, fmt.Sprintf(`%s_request_duration_seconds_count{code="404",host="example.com",method="GET",url="/nonExistentPath"} 1`, defaultSubsystem))

unregisterDefaults(defaultSubsystem)
}

// TestSetPathFor404Logic tests that the url is included in the metric when the 404 response is due to logic
func TestSetPathFor404Logic(t *testing.T) {
unregisterDefaults("myapp")
e := echo.New()

e.Use(NewMiddlewareWithConfig(MiddlewareConfig{DoNotUseRequestPathFor404: true, Subsystem: defaultSubsystem}))
e.GET("/metrics", NewHandler())

e.GET("/sample", echo.NotFoundHandler)

assert.Equal(t, http.StatusNotFound, request(e, "/sample"))

s, code := requestBody(e, "/metrics")
assert.Equal(t, http.StatusOK, code)
assert.NotContains(t, s, fmt.Sprintf(`%s_request_duration_seconds_count{code="404",host="example.com",method="GET",url=""} 1`, defaultSubsystem))
assert.Contains(t, s, fmt.Sprintf(`%s_request_duration_seconds_count{code="404",host="example.com",method="GET",url="/sample"} 1`, defaultSubsystem))

unregisterDefaults(defaultSubsystem)
}

func requestBody(e *echo.Echo, path string) (string, int) {
Expand Down

0 comments on commit 7605e2a

Please sign in to comment.