diff --git a/Readme.md b/Readme.md index c8b93bc..6ec37d6 100644 --- a/Readme.md +++ b/Readme.md @@ -159,6 +159,9 @@ This settings will disable measuring the number of requests being handled concur This setting is a list of paths that will not be measured for the request duration and the response size. They will still be counted in the RequestsInflight metric. +#### IgnoredPathRegexps +This setting is a list of regular expression path patterns that will not be measured for the request duration and the response size. They will still be counted in the RequestsInflight metric. + #### Custom handler ID One of the options that you need to pass when wrapping the handler with the middleware is `handlerID`, this has 2 working ways. diff --git a/middleware/middleware.go b/middleware/middleware.go index 351a0d4..57ee2ae 100644 --- a/middleware/middleware.go +++ b/middleware/middleware.go @@ -7,6 +7,7 @@ package middleware import ( "context" "fmt" + "regexp" "strconv" "time" @@ -35,6 +36,10 @@ type Config struct { // IgnoredPaths is a list of paths that will not be measured for the request duration // and the response size. They will still be counted in the RequestsInflight metric. IgnoredPaths []string + // IgnoredPathRegexps is a list of regular expression path patterns that will not be + // measured for the request duration and the response size. They will still be counted + // in the RequestsInflight metric. + IgnoredPathRegexps []string } func (c *Config) defaults() { @@ -57,6 +62,7 @@ type Middleware struct { disableMeasureSize bool disableMeasureInflight bool ignoredPaths map[string]struct{} + ignoredPathRegexps []*regexp.Regexp } // New returns the a Middleware service. @@ -68,6 +74,11 @@ func New(cfg Config) Middleware { ignPaths[path] = struct{}{} } + ignPathRegexps := make([]*regexp.Regexp, len(cfg.IgnoredPathRegexps)) + for i, pathRegexp := range cfg.IgnoredPathRegexps { + ignPathRegexps[i] = regexp.MustCompile(pathRegexp) + } + m := Middleware{ recorder: cfg.Recorder, service: cfg.Service, @@ -75,6 +86,7 @@ func New(cfg Config) Middleware { disableMeasureSize: cfg.DisableMeasureSize, disableMeasureInflight: cfg.DisableMeasureInflight, ignoredPaths: ignPaths, + ignoredPathRegexps: ignPathRegexps, } return m @@ -107,11 +119,18 @@ func (m Middleware) Measure(handlerID string, reporter Reporter, next func()) { // Start the timer and when finishing measure the duration. start := time.Now() defer func() { - _, shouldIgnore := m.ignoredPaths[reporter.URLPath()] - if shouldIgnore { + path := reporter.URLPath() + + if _, shouldIgnore := m.ignoredPaths[path]; shouldIgnore { return } + for _, ignorePathRegexp := range m.ignoredPathRegexps { + if ignorePathRegexp.MatchString(path) { + return + } + } + duration := time.Since(start) // If we need to group the status code, it uses the diff --git a/middleware/middleware_test.go b/middleware/middleware_test.go index bf2ea72..137a259 100644 --- a/middleware/middleware_test.go +++ b/middleware/middleware_test.go @@ -166,6 +166,33 @@ func TestMiddlewareMeasure(t *testing.T) { mrec.On("ObserveHTTPRequestDuration", mock.Anything, expRepProps, mock.Anything).Once() }, }, + + "Having an ignored path regexp in the config, it should not measure the metrics for the ignored path.": { + handlerID: "test01", + config: func() middleware.Config { + return middleware.Config{ + Service: "svc1", + IgnoredPathRegexps: []string{"/ignored/*"}, + } + }, + mock: func(mrec *mockmetrics.Recorder, mrep *mockmiddleware.Reporter) { + // Reporter mocks. + mrep.On("Context").Once().Return(context.TODO()) + mrep.AssertNotCalled(t, "StatusCode") + mrep.AssertNotCalled(t, "Method") + mrep.AssertNotCalled(t, "BytesWritten") + mrep.On("URLPath").Once().Return("/ignored/path") + + // Recorder mocks. + expProps := metrics.HTTPProperties{Service: "svc1", ID: "test01"} + expRepProps := metrics.HTTPReqProperties{Service: "svc1", ID: "test01", Method: "PATCH", Code: "418"} + + mrec.On("AddInflightRequests", mock.Anything, expProps, 1).Once() + mrec.On("AddInflightRequests", mock.Anything, expProps, -1).Once() + mrec.AssertNotCalled(t, "ObserveHTTPRequestDuration", mock.Anything, expRepProps, mock.Anything) + mrec.AssertNotCalled(t, "ObserveHTTPResponseSize", mock.Anything, expRepProps, int64(42)) + }, + }, } for name, test := range tests {