From 78e1d4ceea81abfb55442b81aedc0d667c494c44 Mon Sep 17 00:00:00 2001 From: Skyenought <1808644906@qq.com> Date: Wed, 2 Nov 2022 17:18:53 +0800 Subject: [PATCH] :sparkles: Add customTags in logger middleware Config --- middleware/logger/README.md | 18 ++++- middleware/logger/config.go | 11 ++++ middleware/logger/logger.go | 110 +++++++++---------------------- middleware/logger/logger_test.go | 29 ++++++++ middleware/logger/tag.go | 101 ++++++++++++++++++++++++++++ 5 files changed, 190 insertions(+), 79 deletions(-) create mode 100644 middleware/logger/tag.go diff --git a/middleware/logger/README.md b/middleware/logger/README.md index 63c1530fda5..7b69d916f9c 100644 --- a/middleware/logger/README.md +++ b/middleware/logger/README.md @@ -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) @@ -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 @@ -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 diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 08c5ad51d95..6497aae4d1c 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -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 @@ -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 } diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 566b685d220..9815860162b 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -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, "${", "}") @@ -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 @@ -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:])) diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 43522fa3008..798aeeef587 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -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()) +} diff --git a/middleware/logger/tag.go b/middleware/logger/tag.go new file mode 100644 index 00000000000..ac3a99bc2fe --- /dev/null +++ b/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("-") + }, +}