Skip to content

Commit

Permalink
feat(logger): ability to skip logs based on user-defined logic (#3593)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
palvaneh authored Feb 2, 2024
1 parent a64286a commit c6ae2e6
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 21 deletions.
38 changes: 38 additions & 0 deletions docs/doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -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" "
```

### 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

By default, logs output on console should be colorized depending on the detected TTY.
Expand Down
51 changes: 30 additions & 21 deletions logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,15 @@ type LoggerConfig struct {
// SkipPaths is an url path array which logs are not written.
// Optional.
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
type LogFormatter func(params LogFormatterParams) string

Expand Down Expand Up @@ -241,32 +248,34 @@ func LoggerWithConfig(conf LoggerConfig) HandlerFunc {
// Process request
c.Next()

// Log only when path is not being skipped
if _, ok := skip[path]; !ok {
param := LogFormatterParams{
Request: c.Request,
isTerm: isTerm,
Keys: c.Keys,
}

// Stop timer
param.TimeStamp = time.Now()
param.Latency = param.TimeStamp.Sub(start)
// Log only when it is not being skipped
if _, ok := skip[path]; ok || (conf.Skip != nil && conf.Skip(c)) {
return
}

param.ClientIP = c.ClientIP()
param.Method = c.Request.Method
param.StatusCode = c.Writer.Status()
param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()
param := LogFormatterParams{
Request: c.Request,
isTerm: isTerm,
Keys: c.Keys,
}

param.BodySize = c.Writer.Size()
// Stop timer
param.TimeStamp = time.Now()
param.Latency = param.TimeStamp.Sub(start)

if raw != "" {
path = path + "?" + raw
}
param.ClientIP = c.ClientIP()
param.Method = c.Request.Method
param.StatusCode = c.Writer.Status()
param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()

param.Path = path
param.BodySize = c.Writer.Size()

fmt.Fprint(out, formatter(param))
if raw != "" {
path = path + "?" + raw
}

param.Path = path

fmt.Fprint(out, formatter(param))
}
}
20 changes: 20 additions & 0 deletions logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,26 @@ func TestLoggerWithConfigSkippingPaths(t *testing.T) {
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) {
New()
assert.Equal(t, autoColor, consoleColorMode)
Expand Down

0 comments on commit c6ae2e6

Please sign in to comment.