feat(logger): ability to skip logs based on user-defined logic (#3593)

* log skipper

* do not call time.now() if logging should be skipped

* do not ignore skip func delay in latency calculation

* write docs

* write test
This commit is contained in:
Ghobad 2024-02-02 05:22:26 +03:30 committed by GitHub
parent a64286a776
commit c6ae2e6966
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 93 additions and 26 deletions

View File

@ -508,6 +508,44 @@ Sample Output
::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" " ::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" "
``` ```
### Skip logging
```go
func main() {
router := gin.New()
// skip logging for desired paths by setting SkipPaths in LoggerConfig
loggerConfig := gin.LoggerConfig{SkipPaths: []string{"/metrics"}}
// skip logging based on your logic by setting Skip func in LoggerConfig
loggerConfig.Skip = func(c *gin.Context) bool {
// as an example skip non server side errors
return c.Writer.Status() < http.StatusInternalServerError
}
engine.Use(gin.LoggerWithConfig(loggerConfig))
router.Use(gin.Recovery())
// skipped
router.GET("/metrics", func(c *gin.Context) {
c.Status(http.StatusNotImplemented)
})
// skipped
router.GET("/ping", func(c *gin.Context) {
c.String(http.StatusOK, "pong")
})
// not skipped
router.GET("/data", func(c *gin.Context) {
c.Status(http.StatusNotImplemented)
})
router.Run(":8080")
}
```
### Controlling Log output coloring ### Controlling Log output coloring
By default, logs output on console should be colorized depending on the detected TTY. By default, logs output on console should be colorized depending on the detected TTY.

View File

@ -47,8 +47,15 @@ type LoggerConfig struct {
// SkipPaths is an url path array which logs are not written. // SkipPaths is an url path array which logs are not written.
// Optional. // Optional.
SkipPaths []string SkipPaths []string
// Skip is a Skipper that indicates which logs should not be written.
// Optional.
Skip Skipper
} }
// Skipper is a function to skip logs based on provided Context
type Skipper func(c *Context) bool
// LogFormatter gives the signature of the formatter function passed to LoggerWithFormatter // LogFormatter gives the signature of the formatter function passed to LoggerWithFormatter
type LogFormatter func(params LogFormatterParams) string type LogFormatter func(params LogFormatterParams) string
@ -241,32 +248,34 @@ func LoggerWithConfig(conf LoggerConfig) HandlerFunc {
// Process request // Process request
c.Next() c.Next()
// Log only when path is not being skipped // Log only when it is not being skipped
if _, ok := skip[path]; !ok { if _, ok := skip[path]; ok || (conf.Skip != nil && conf.Skip(c)) {
param := LogFormatterParams{ return
Request: c.Request,
isTerm: isTerm,
Keys: c.Keys,
}
// Stop timer
param.TimeStamp = time.Now()
param.Latency = param.TimeStamp.Sub(start)
param.ClientIP = c.ClientIP()
param.Method = c.Request.Method
param.StatusCode = c.Writer.Status()
param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()
param.BodySize = c.Writer.Size()
if raw != "" {
path = path + "?" + raw
}
param.Path = path
fmt.Fprint(out, formatter(param))
} }
param := LogFormatterParams{
Request: c.Request,
isTerm: isTerm,
Keys: c.Keys,
}
// Stop timer
param.TimeStamp = time.Now()
param.Latency = param.TimeStamp.Sub(start)
param.ClientIP = c.ClientIP()
param.Method = c.Request.Method
param.StatusCode = c.Writer.Status()
param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()
param.BodySize = c.Writer.Size()
if raw != "" {
path = path + "?" + raw
}
param.Path = path
fmt.Fprint(out, formatter(param))
} }
} }

View File

@ -415,6 +415,26 @@ func TestLoggerWithConfigSkippingPaths(t *testing.T) {
assert.Contains(t, buffer.String(), "") assert.Contains(t, buffer.String(), "")
} }
func TestLoggerWithConfigSkipper(t *testing.T) {
buffer := new(strings.Builder)
router := New()
router.Use(LoggerWithConfig(LoggerConfig{
Output: buffer,
Skip: func(c *Context) bool {
return c.Writer.Status() == http.StatusNoContent
},
}))
router.GET("/logged", func(c *Context) { c.Status(http.StatusOK) })
router.GET("/skipped", func(c *Context) { c.Status(http.StatusNoContent) })
PerformRequest(router, "GET", "/logged")
assert.Contains(t, buffer.String(), "200")
buffer.Reset()
PerformRequest(router, "GET", "/skipped")
assert.Contains(t, buffer.String(), "")
}
func TestDisableConsoleColor(t *testing.T) { func TestDisableConsoleColor(t *testing.T) {
New() New()
assert.Equal(t, autoColor, consoleColorMode) assert.Equal(t, autoColor, consoleColorMode)