Skip to content

Commit

Permalink
✨ Add customTags in logger middleware Config
Browse files Browse the repository at this point in the history
  • Loading branch information
Skyenought committed Nov 2, 2022
1 parent 24a6170 commit 78e1d4c
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 79 deletions.
18 changes: 17 additions & 1 deletion middleware/logger/README.md
Expand Up @@ -11,6 +11,7 @@ Logger middleware for [Fiber](https://github.com/gofiber/fiber) that logs HTTP r
- [Logging Request ID](#logging-request-id)
- [Changing TimeZone & TimeFormat](#changing-timezone--timeformat)
- [Custom File Writer](#custom-file-writer)
- [Add Custom Tags](#add-custom-tags)
- [Config](#config)
- [Default Config](#default-config-1)
- [Constants](#constants)
Expand Down Expand Up @@ -75,6 +76,16 @@ app.Use(logger.New(logger.Config{
Output: file,
}))
```
### Add Custom Tags
```go
app.Use(logger.New(logger.Config{
CustomTags: map[string]logger.LogFunc{
"custom_tag": func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString("it is a custom tag")
},
},
}))
```

## Config
```go
Expand All @@ -85,11 +96,16 @@ type Config struct {
// Optional. Default: nil
Next func(c *fiber.Ctx) bool

// CustomTags defines the custom tag action
//
// Optional. Default: map[string]LogFunc{}
CustomTags map[string]LogFunc

// Format defines the logging tags
//
// Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n
Format string

// TimeFormat https://programming.guide/go/format-parse-string-time-date-example.html
//
// Optional. Default: 15:04:05
Expand Down
11 changes: 11 additions & 0 deletions middleware/logger/config.go
Expand Up @@ -16,6 +16,11 @@ type Config struct {
// Optional. Default: nil
Next func(c *fiber.Ctx) bool

// CustomTags defines the custom tag action
//
// Optional. Default: map[string]LogFunc
CustomTags map[string]LogFunc

// Format defines the logging tags
//
// Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n
Expand Down Expand Up @@ -105,5 +110,11 @@ func configDefault(config ...Config) Config {
if cfg.Output == nil {
cfg.Output = ConfigDefault.Output
}
// Set custom tags
if len(cfg.CustomTags) != 0 {
for k, v := range cfg.CustomTags {
tagMap[k] = v
}
}
return cfg
}
110 changes: 32 additions & 78 deletions middleware/logger/logger.go
Expand Up @@ -77,6 +77,24 @@ func New(config ...Config) fiber.Handler {
// Check if format contains latency
cfg.enableLatency = strings.Contains(cfg.Format, "${latency}")

// Add TagStatus Config
tagMap[TagStatus] = func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
colors := c.App().Config().ColorScheme
if cfg.enableColors {
return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset))
}
return appendInt(buf, c.Response().StatusCode())
}

// Add TagMethod Config
tagMap[TagMethod] = func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
colors := c.App().Config().ColorScheme
if cfg.enableColors {
return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset))
}
return buf.WriteString(c.Method())
}

// Create template parser
tmpl := fasttemplate.New(cfg.Format, "${", "}")

Expand All @@ -97,6 +115,9 @@ func New(config ...Config) fiber.Handler {
// Set PID once
pid := strconv.Itoa(os.Getpid())

tagMap[TagPid] = func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(pid)
}
// Set variables
var (
once sync.Once
Expand Down Expand Up @@ -193,86 +214,19 @@ func New(config ...Config) fiber.Handler {
return nil
}

tagMap[TagTime] = func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(timestamp.Load().(string))
}

tagMap[TagLatency] = func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(fmt.Sprintf("%7v", stop.Sub(start).Round(time.Millisecond)))
}

// Loop over template tags to replace it with the correct value
_, err = tmpl.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) {
switch tag {
case TagTime:
return buf.WriteString(timestamp.Load().(string))
case TagReferer:
return buf.WriteString(c.Get(fiber.HeaderReferer))
case TagProtocol:
return buf.WriteString(c.Protocol())
case TagPid:
return buf.WriteString(pid)
case TagPort:
return buf.WriteString(c.Port())
case TagIP:
return buf.WriteString(c.IP())
case TagIPs:
return buf.WriteString(c.Get(fiber.HeaderXForwardedFor))
case TagHost:
return buf.WriteString(c.Hostname())
case TagPath:
return buf.WriteString(c.Path())
case TagURL:
return buf.WriteString(c.OriginalURL())
case TagUA:
return buf.WriteString(c.Get(fiber.HeaderUserAgent))
case TagLatency:
return buf.WriteString(fmt.Sprintf("%7v", stop.Sub(start).Round(time.Millisecond)))
case TagBody:
return buf.Write(c.Body())
case TagBytesReceived:
return appendInt(buf, len(c.Request().Body()))
case TagBytesSent:
return appendInt(buf, len(c.Response().Body()))
case TagRoute:
return buf.WriteString(c.Route().Path)
case TagStatus:
if cfg.enableColors {
return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset))
}
return appendInt(buf, c.Response().StatusCode())
case TagResBody:
return buf.Write(c.Response().Body())
case TagReqHeaders:
reqHeaders := make([]string, 0)
for k, v := range c.GetReqHeaders() {
reqHeaders = append(reqHeaders, k+"="+v)
}
return buf.Write([]byte(strings.Join(reqHeaders, "&")))
case TagQueryStringParams:
return buf.WriteString(c.Request().URI().QueryArgs().String())
case TagMethod:
if cfg.enableColors {
return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset))
}
return buf.WriteString(c.Method())
case TagBlack:
return buf.WriteString(colors.Black)
case TagRed:
return buf.WriteString(colors.Red)
case TagGreen:
return buf.WriteString(colors.Green)
case TagYellow:
return buf.WriteString(colors.Yellow)
case TagBlue:
return buf.WriteString(colors.Blue)
case TagMagenta:
return buf.WriteString(colors.Magenta)
case TagCyan:
return buf.WriteString(colors.Cyan)
case TagWhite:
return buf.WriteString(colors.White)
case TagReset:
return buf.WriteString(colors.Reset)
case TagError:
if chainErr != nil {
return buf.WriteString(chainErr.Error())
}
return buf.WriteString("-")
default:
// Check if we have a value tag i.e.: "reqHeader:x-key"
if logFunc, ok := tagMap[tag]; ok {
return logFunc(buf, c, w, tag)
} else {
switch {
case strings.HasPrefix(tag, TagReqHeader):
return buf.WriteString(c.Get(tag[10:]))
Expand Down
29 changes: 29 additions & 0 deletions middleware/logger/logger_test.go
Expand Up @@ -360,3 +360,32 @@ func Test_ReqHeader_Header(t *testing.T) {
utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode)
utils.AssertEqual(t, "Hello fiber!", buf.String())
}

// go test -run Test_CustomTags
func Test_CustomTags(t *testing.T) {
customTag := "it is a custom tag"

buf := bytebufferpool.Get()
defer bytebufferpool.Put(buf)

app := fiber.New()
app.Use(New(Config{
Format: "${custom_tag}",
CustomTags: map[string]LogFunc{
"custom_tag": func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(customTag)
},
},
Output: buf,
}))
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello fiber!")
})
reqHeaderReq := httptest.NewRequest("GET", "/", nil)
reqHeaderReq.Header.Add("test", "Hello fiber!")
resp, err := app.Test(reqHeaderReq)

utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode)
utils.AssertEqual(t, customTag, buf.String())
}
101 changes: 101 additions & 0 deletions middleware/logger/tag.go
@@ -0,0 +1,101 @@
package logger

import (
"io"
"strings"

"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/internal/bytebufferpool"
)

type LogFunc func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error)

// tagMap use to storage how to log content when parse Tag.
var tagMap = map[string]LogFunc{
TagReferer: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.Get(fiber.HeaderReferer))
},
TagProtocol: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.Protocol())
},
TagPort: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.Port())

},
TagIP: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.IP())
},
TagIPs: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.Get(fiber.HeaderXForwardedFor))
},
TagHost: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.Hostname())
},
TagPath: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.Path())
},
TagURL: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.OriginalURL())
},
TagUA: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.Get(fiber.HeaderUserAgent))
},
TagBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.Write(c.Body())
},
TagBytesReceived: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return appendInt(buf, len(c.Request().Body()))
},
TagBytesSent: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return appendInt(buf, len(c.Response().Body()))
},
TagRoute: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.Route().Path)
},
TagResBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.Write(c.Response().Body())
},
TagReqHeaders: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
reqHeaders := make([]string, 0)
for k, v := range c.GetReqHeaders() {
reqHeaders = append(reqHeaders, k+"="+v)
}
return buf.Write([]byte(strings.Join(reqHeaders, "&")))
},
TagQueryStringParams: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.Request().URI().QueryArgs().String())
},
TagBlack: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.App().Config().ColorScheme.Black)
},
TagRed: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.App().Config().ColorScheme.Red)
},
TagGreen: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.App().Config().ColorScheme.Green)
},
TagYellow: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.App().Config().ColorScheme.Yellow)
},
TagBlue: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.App().Config().ColorScheme.Blue)
},
TagMagenta: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.App().Config().ColorScheme.Magenta)
},
TagCyan: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.App().Config().ColorScheme.Cyan)
},
TagWhite: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.App().Config().ColorScheme.White)
},
TagReset: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
return buf.WriteString(c.App().Config().ColorScheme.Reset)
},
TagError: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) {
if err := c.Next(); err != nil {
return buf.WriteString(err.Error())
}
return buf.WriteString("-")
},
}

0 comments on commit 78e1d4c

Please sign in to comment.