Skip to content

Commit e412c28

Browse files
committed
Skip access log entries if path is excluded
1 parent bbc016e commit e412c28

File tree

3 files changed

+112
-13
lines changed

3 files changed

+112
-13
lines changed

helper.go

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,11 @@ func SetWithConfig(level string, config *LogConfig) error {
6868

6969
// Access logs an access entry with call duration and status code
7070
func Access(r *http.Request, start time.Time, statusCode int) {
71-
e := access(r, start, statusCode, nil)
71+
access(logrus.InfoLevel, r, start, statusCode)
72+
}
73+
74+
func access(level logrus.Level, r *http.Request, start time.Time, statusCode int) {
75+
e := createAccessEntry(r, start, statusCode, nil)
7276

7377
var msg string
7478
if len(r.URL.RawQuery) == 0 {
@@ -77,17 +81,19 @@ func Access(r *http.Request, start time.Time, statusCode int) {
7781
msg = fmt.Sprintf("%v ->%v %v?%s", statusCode, r.Method, r.URL.Path, r.URL.RawQuery)
7882
}
7983

84+
e.Log(accessLogLevelFor(level, r, statusCode), msg)
85+
}
86+
87+
func accessLogLevelFor(level logrus.Level, r *http.Request, statusCode int) logrus.Level {
8088
if statusCode >= 200 && statusCode <= 399 {
8189
if isHealthRequest(r) {
82-
e.Debug(msg)
83-
} else {
84-
e.Info(msg)
90+
return logrus.DebugLevel
8591
}
92+
return level
8693
} else if statusCode >= 400 && statusCode <= 499 {
87-
e.Warn(msg)
88-
} else {
89-
e.Error(msg)
94+
return logrus.WarnLevel
9095
}
96+
return logrus.ErrorLevel
9197
}
9298

9399
func isHealthRequest(r *http.Request) bool {
@@ -96,16 +102,16 @@ func isHealthRequest(r *http.Request) bool {
96102

97103
// AccessError logs an error while accessing
98104
func AccessError(r *http.Request, start time.Time, err error) {
99-
e := access(r, start, 0, err)
105+
e := createAccessEntry(r, start, 0, err)
100106
e.Errorf("ERROR ->%v %v", r.Method, r.URL.Path)
101107
}
102108

103109
func AccessAborted(r *http.Request, start time.Time) {
104-
e := access(r, start, 0, nil)
110+
e := createAccessEntry(r, start, 0, nil)
105111
e.Infof("ABORTED ->%v %v", r.Method, r.URL.Path)
106112
}
107113

108-
func access(r *http.Request, start time.Time, statusCode int, err error) *Entry {
114+
func createAccessEntry(r *http.Request, start time.Time, statusCode int, err error) *Entry {
109115
url := r.URL.Path
110116
if r.URL.RawQuery != "" {
111117
url += "?" + r.URL.RawQuery

log_middleware.go

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,64 @@ import (
44
"errors"
55
"fmt"
66
"net/http"
7+
"regexp"
78
"runtime"
89
"strings"
910
"time"
1011

12+
"github.com/sirupsen/logrus"
1113
"github.com/snabble/go-logging/v2/tracex"
1214
)
1315

16+
type LogMiddlewareConfig struct {
17+
// SkipSuccessfulRequestsMatching is a list of go reqular
18+
// expressions, the access log is skipped if for request where the
19+
// request path matches the expression.
20+
SkipSuccessfulRequestsMatching []string
21+
}
22+
1423
type LogMiddleware struct {
1524
Next http.Handler
25+
26+
skipCache []*regexp.Regexp
1627
}
1728

1829
func NewLogMiddleware(next http.Handler) http.Handler {
30+
handler, _ := AddLogMiddleware(next, LogMiddlewareConfig{})
31+
return handler
32+
}
33+
34+
func AddLogMiddleware(next http.Handler, cfg LogMiddlewareConfig) (http.Handler, error) {
35+
skipCache, err := buildSkipCache(cfg)
36+
if err != nil {
37+
return nil, err
38+
}
39+
40+
middleware := &LogMiddleware{
41+
Next: next,
42+
skipCache: skipCache,
43+
}
44+
1945
if Log.config.EnableTraces {
20-
return tracex.NewHandler(&LogMiddleware{Next: next}, "common")
46+
return tracex.NewHandler(middleware, "common"), nil
2147
}
22-
return &LogMiddleware{Next: next}
48+
49+
return middleware, nil
50+
}
51+
52+
func buildSkipCache(cfg LogMiddlewareConfig) ([]*regexp.Regexp, error) {
53+
var skipCache []*regexp.Regexp
54+
55+
for _, expr := range cfg.SkipSuccessfulRequestsMatching {
56+
compiled, err := regexp.Compile(expr)
57+
if err != nil {
58+
return nil, fmt.Errorf("invalid matcher: '%s': %w", expr, err)
59+
}
60+
61+
skipCache = append(skipCache, compiled)
62+
}
63+
64+
return skipCache, nil
2365
}
2466

2567
func (mw *LogMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -39,7 +81,21 @@ func (mw *LogMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
3981
lrw := &logResponseWriter{ResponseWriter: w}
4082
mw.Next.ServeHTTP(lrw, r)
4183

42-
Access(r, start, lrw.statusCode)
84+
level := logrus.InfoLevel
85+
if mw.isSkipped(r.URL.Path) {
86+
level = logrus.DebugLevel
87+
}
88+
89+
access(level, r, start, lrw.statusCode)
90+
}
91+
92+
func (mw *LogMiddleware) isSkipped(path string) bool {
93+
for _, exp := range mw.skipCache {
94+
if exp.MatchString(path) {
95+
return true
96+
}
97+
}
98+
return false
4399
}
44100

45101
// identifyLogOrigin returns the location, where a panic was raised

log_middleware_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,43 @@ func Test_LogMiddleware_Log_implicit200(t *testing.T) {
133133
a.Equal("info", data.Level)
134134
}
135135

136+
func Test_LogMiddleware_Log_SkipSuccessfulRequestsMatching(t *testing.T) {
137+
for _, test := range []struct {
138+
name string
139+
url string
140+
code int
141+
logged bool
142+
}{
143+
{"skipped", "https://www.example.org/some/suffix", http.StatusOK, false},
144+
{"status not ok", "https://www.example.org/some/suffix", http.StatusInternalServerError, true},
145+
{"not matched", "https://www.example.org/some/other", http.StatusOK, true},
146+
} {
147+
t.Run(test.name, func(t *testing.T) {
148+
logBuffer := bytes.NewBuffer(nil)
149+
Log.Out = logBuffer
150+
151+
lm, err := AddLogMiddleware(
152+
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
153+
w.WriteHeader(test.code)
154+
_, _ = w.Write([]byte("OK++"))
155+
}),
156+
LogMiddlewareConfig{
157+
SkipSuccessfulRequestsMatching: []string{
158+
"/suffix$",
159+
},
160+
},
161+
)
162+
require.NoError(t, err)
163+
164+
req, _ := http.NewRequest(http.MethodGet, test.url, nil)
165+
166+
lm.ServeHTTP(httptest.NewRecorder(), req)
167+
168+
assert.Equal(t, test.logged, logBuffer.Len() > 0)
169+
})
170+
}
171+
}
172+
136173
func Test_LogMiddleware_Log_HealthRequestAreNotLogged(t *testing.T) {
137174
tests := []struct {
138175
name string

0 commit comments

Comments
 (0)