Skip to content

Commit 7605e2a

Browse files
authored
Conditionally set url label for 404 responses (#111)
* conditionally set url label for 404 responses
1 parent fdc9c9a commit 7605e2a

File tree

2 files changed

+44
-1
lines changed

2 files changed

+44
-1
lines changed

echoprometheus/prometheus.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ type MiddlewareConfig struct {
7373
AfterNext func(c echo.Context, err error)
7474

7575
timeNow func() time.Time
76+
77+
// If DoNotUseRequestPathFor404 is true, all 404 responses (due to non-matching route) will have the same `url` label and
78+
// thus won't generate new metrics.
79+
DoNotUseRequestPathFor404 bool
7680
}
7781

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

248252
url := c.Path() // contains route path ala `/users/:id`
249-
if url == "" {
253+
if url == "" && !conf.DoNotUseRequestPathFor404 {
250254
// as of Echo v4.10.1 path is empty for 404 cases (when router did not find any matching routes)
251255
// in this case we use actual path from request to have some distinction in Prometheus
252256
url = c.Request().URL.Path

echoprometheus/prometheus_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,45 @@ func TestRunPushGatewayGatherer(t *testing.T) {
275275

276276
assert.EqualError(t, err, "code=400, message=post metrics request did not succeed")
277277
assert.True(t, receivedMetrics)
278+
unregisterDefaults("myapp")
279+
}
280+
281+
// TestSetPathFor404NoMatchingRoute tests that the url is not included in the metric when
282+
// the 404 response is due to no matching route
283+
func TestSetPathFor404NoMatchingRoute(t *testing.T) {
284+
e := echo.New()
285+
286+
e.Use(NewMiddlewareWithConfig(MiddlewareConfig{DoNotUseRequestPathFor404: true, Subsystem: defaultSubsystem}))
287+
e.GET("/metrics", NewHandler())
288+
289+
assert.Equal(t, http.StatusNotFound, request(e, "/nonExistentPath"))
290+
291+
s, code := requestBody(e, "/metrics")
292+
assert.Equal(t, http.StatusOK, code)
293+
assert.Contains(t, s, fmt.Sprintf(`%s_request_duration_seconds_count{code="404",host="example.com",method="GET",url=""} 1`, defaultSubsystem))
294+
assert.NotContains(t, s, fmt.Sprintf(`%s_request_duration_seconds_count{code="404",host="example.com",method="GET",url="/nonExistentPath"} 1`, defaultSubsystem))
295+
296+
unregisterDefaults(defaultSubsystem)
297+
}
298+
299+
// TestSetPathFor404Logic tests that the url is included in the metric when the 404 response is due to logic
300+
func TestSetPathFor404Logic(t *testing.T) {
301+
unregisterDefaults("myapp")
302+
e := echo.New()
303+
304+
e.Use(NewMiddlewareWithConfig(MiddlewareConfig{DoNotUseRequestPathFor404: true, Subsystem: defaultSubsystem}))
305+
e.GET("/metrics", NewHandler())
306+
307+
e.GET("/sample", echo.NotFoundHandler)
308+
309+
assert.Equal(t, http.StatusNotFound, request(e, "/sample"))
310+
311+
s, code := requestBody(e, "/metrics")
312+
assert.Equal(t, http.StatusOK, code)
313+
assert.NotContains(t, s, fmt.Sprintf(`%s_request_duration_seconds_count{code="404",host="example.com",method="GET",url=""} 1`, defaultSubsystem))
314+
assert.Contains(t, s, fmt.Sprintf(`%s_request_duration_seconds_count{code="404",host="example.com",method="GET",url="/sample"} 1`, defaultSubsystem))
315+
316+
unregisterDefaults(defaultSubsystem)
278317
}
279318

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

0 commit comments

Comments
 (0)