From ee1db46f271ec47ad075067d5a22350ea4adb801 Mon Sep 17 00:00:00 2001 From: Skyenought <1808644906@qq.com> Date: Thu, 17 Nov 2022 12:19:18 +0800 Subject: [PATCH 1/6] :sparkles: Add customTags in logger middleware Config --- middleware/logger/README.md | 18 +++- middleware/logger/config.go | 162 +++++++++++++++++++++++++++++++ middleware/logger/logger.go | 159 +++++++++++------------------- middleware/logger/logger_test.go | 32 +++++- 4 files changed, 264 insertions(+), 107 deletions(-) diff --git a/middleware/logger/README.md b/middleware/logger/README.md index 63c1530fda..7b69d916f9 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 0e8aaa5caf..9462c544ff 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -1,6 +1,8 @@ package logger import ( + "fmt" + "github.com/gofiber/fiber/v2/internal/bytebufferpool" "io" "os" "strings" @@ -22,6 +24,11 @@ type Config struct { // Optional. Default: a function that does nothing. Done func(c *fiber.Ctx, logString []byte) + // tagFunctions 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 @@ -50,6 +57,14 @@ type Config struct { enableColors bool enableLatency bool timeZoneLocation *time.Location + tagFunctions map[string]LogFunc +} + +type LogFunc func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) + +type TagMap struct { + Tag string + TagFunction LogFunc } // ConfigDefault is the default config @@ -115,5 +130,152 @@ func configDefault(config ...Config) Config { if cfg.Output == nil { cfg.Output = ConfigDefault.Output } + // Set custom tags + cfg.tagFunctions = 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 s := c.Context().UserValue("loggerChainError").(string); s != "" { + return buf.WriteString(s) + } + return buf.WriteString("-") + }, + TagReqHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { + return buf.WriteString(c.Get(tag[10:])) + }, + TagHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { + return buf.WriteString(c.Get(tag[7:])) + }, + TagRespHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { + return buf.WriteString(c.GetRespHeader(tag[11:])) + }, + TagQuery: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { + return buf.WriteString(c.Query(tag[6:])) + }, + TagForm: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { + return buf.WriteString(c.FormValue(tag[5:])) + }, + TagCookie: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { + return buf.WriteString(c.Cookies(tag[7:])) + }, + TagLocals: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { + switch v := c.Locals(tag[7:]).(type) { + case []byte: + return buf.Write(v) + case string: + return buf.WriteString(v) + case nil: + return 0, nil + default: + return buf.WriteString(fmt.Sprintf("%v", v)) + } + }, + 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()) + }, + 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()) + }, + TagPid: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { + return buf.WriteString(pid) + }, + TagLatency: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { + ctx := c.Context() + latency := ctx.UserValue(loggerStop).(time.Time).Sub(ctx.UserValue(loggerStart).(time.Time)) + return buf.WriteString(fmt.Sprintf("%7v", latency)) + }, + TagTime: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { + return buf.WriteString(timestamp.Load().(string)) + }, + } + for k, v := range cfg.CustomTags { + cfg.tagFunctions[k] = v + } return cfg } diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index dc348bfebe..acc2f95411 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -2,6 +2,7 @@ package logger import ( "fmt" + "github.com/gofiber/fiber/v2/utils" "io" "os" "strconv" @@ -62,6 +63,14 @@ const ( TagReset = "reset" ) +const ( + loggerStart = iota + loggerStop +) + +var pid string +var timestamp atomic.Value + // New creates a new middleware handler func New(config ...Config) fiber.Handler { // Set default config @@ -82,7 +91,6 @@ func New(config ...Config) fiber.Handler { tmpl := fasttemplate.New(cfg.Format, "${", "}") // Create correct timeformat - var timestamp atomic.Value timestamp.Store(time.Now().In(cfg.timeZoneLocation).Format(cfg.TimeFormat)) // Update date/time every 500 milliseconds in a separate go routine @@ -96,7 +104,7 @@ func New(config ...Config) fiber.Handler { } // Set PID once - pid := strconv.Itoa(os.Getpid()) + pid = strconv.Itoa(os.Getpid()) // Set variables var ( @@ -114,6 +122,9 @@ func New(config ...Config) fiber.Handler { } errPadding := 15 errPaddingStr := strconv.Itoa(errPadding) + + tagFunctionMap := cfg.tagFunctions + // Return new handler return func(c *fiber.Ctx) (err error) { // Don't execute middleware if Next returns true @@ -145,6 +156,7 @@ func New(config ...Config) fiber.Handler { // Set latency start time if cfg.enableLatency { start = time.Now() + c.Context().SetUserValue(loggerStart, start) } // Handle request, store err for logging @@ -152,6 +164,7 @@ func New(config ...Config) fiber.Handler { // Manually call error handler if chainErr != nil { + c.Context().SetUserValue("loggerChainError", chainErr.Error()) if err := errHandler(c, chainErr); err != nil { _ = c.SendStatus(fiber.StatusInternalServerError) } @@ -160,6 +173,7 @@ func New(config ...Config) fiber.Handler { // Set latency stop time if cfg.enableLatency { stop = time.Now() + c.Context().SetUserValue(loggerStop, stop) } // Get new buffer @@ -198,110 +212,16 @@ func New(config ...Config) fiber.Handler { // 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" - switch { - case strings.HasPrefix(tag, TagReqHeader): - return buf.WriteString(c.Get(tag[10:])) - case strings.HasPrefix(tag, TagHeader): - return buf.WriteString(c.Get(tag[7:])) - case strings.HasPrefix(tag, TagRespHeader): - return buf.WriteString(c.GetRespHeader(tag[11:])) - case strings.HasPrefix(tag, TagQuery): - return buf.WriteString(c.Query(tag[6:])) - case strings.HasPrefix(tag, TagForm): - return buf.WriteString(c.FormValue(tag[5:])) - case strings.HasPrefix(tag, TagCookie): - return buf.WriteString(c.Cookies(tag[7:])) - case strings.HasPrefix(tag, TagLocals): - switch v := c.Locals(tag[7:]).(type) { - case []byte: - return buf.Write(v) - case string: - return buf.WriteString(v) - case nil: - return 0, nil - default: - return buf.WriteString(fmt.Sprintf("%v", v)) - } - } + if logFunc, ok := tagFunctionMap[tag]; ok { + return logFunc(buf, c, w, tag) } + + if index := strings.Index(tag, ":"); index != -1 { + result := subStr(tag, 0, index+1) + logFunc := tagFunctionMap[result] + return logFunc(buf, c, w, tag) + } + return 0, nil }) // Also write errors to the buffer @@ -333,3 +253,32 @@ func appendInt(buf *bytebufferpool.ByteBuffer, v int) (int, error) { buf.B = fasthttp.AppendUint(buf.B, v) return len(buf.B) - old, nil } + +func subStr(str string, start int, length int) (result string) { + s := utils.UnsafeBytes(str) + total := len(s) + if total == 0 { + return + } + // 允许从尾部开始计算 + if start < 0 { + start = total + start + if start < 0 { + return + } + } + if start > total { + return + } + if length < 0 { + length = total + } + + end := start + length + if end > total { + result = utils.UnsafeString(s[start:]) + } else { + result = utils.UnsafeString(s[start:end]) + } + return +} diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index ba566d2c2e..0950bb8f3c 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -290,10 +290,11 @@ func Benchmark_Logger(b *testing.B) { app := fiber.New() app.Use(New(Config{ - Format: "${bytesReceived} ${bytesSent} ${status}", + Format: "${bytesReceived} ${bytesSent} ${status} ${reqHeader:test}", Output: io.Discard, })) app.Get("/", func(c *fiber.Ctx) error { + c.Set("test", "test") return c.SendString("Hello, World!") }) @@ -383,3 +384,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()) +} From 70c8c7112b9f0176755f5783fdc32ec4e49f2393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Thu, 17 Nov 2022 12:58:48 +0100 Subject: [PATCH 2/6] improve tags with parameter --- middleware/logger/logger.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index acc2f95411..f2f32fc0d7 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -2,7 +2,6 @@ package logger import ( "fmt" - "github.com/gofiber/fiber/v2/utils" "io" "os" "strconv" @@ -11,6 +10,8 @@ import ( "sync/atomic" "time" + "github.com/gofiber/fiber/v2/utils" + "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "github.com/valyala/fasthttp" @@ -217,9 +218,9 @@ func New(config ...Config) fiber.Handler { } if index := strings.Index(tag, ":"); index != -1 { - result := subStr(tag, 0, index+1) - logFunc := tagFunctionMap[result] - return logFunc(buf, c, w, tag) + if logFunc, ok := tagFunctionMap[tag[0:index+1]]; ok { + return logFunc(buf, c, w, tag) + } } return 0, nil From 05dabe7ffa472cad2ddc7c4fb2a7e7f4d9354d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Thu, 17 Nov 2022 17:27:49 +0100 Subject: [PATCH 3/6] improve logger performance --- middleware/logger/config.go | 175 +++-------------------------- middleware/logger/logger.go | 60 +++------- middleware/logger/logger_test.go | 2 +- middleware/logger/tags.go | 165 +++++++++++++++++++++++++++ middleware/logger/templateChain.go | 50 +++++++++ 5 files changed, 243 insertions(+), 209 deletions(-) create mode 100644 middleware/logger/tags.go create mode 100644 middleware/logger/templateChain.go diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 9462c544ff..13b1a44e74 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -1,14 +1,13 @@ package logger import ( - "fmt" - "github.com/gofiber/fiber/v2/internal/bytebufferpool" "io" "os" "strings" "time" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/internal/bytebufferpool" ) // Config defines the config for middleware. @@ -57,15 +56,15 @@ type Config struct { enableColors bool enableLatency bool timeZoneLocation *time.Location - tagFunctions map[string]LogFunc } -type LogFunc func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) +const ( + startTag = "${" + endTag = "}" + paramSeparator = ":" +) -type TagMap struct { - Tag string - TagFunction LogFunc -} +type LogFunc func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) // ConfigDefault is the default config var ConfigDefault = Config{ @@ -80,11 +79,8 @@ var ConfigDefault = Config{ } // Function to check if the logger format is compatible for coloring -func validCustomFormat(format string) bool { +func checkColorEnable(format string) bool { validTemplates := []string{"${status}", "${method}"} - if format == "" { - return true - } for _, template := range validTemplates { if strings.Contains(format, template) { return true @@ -103,11 +99,6 @@ func configDefault(config ...Config) Config { // Override default config cfg := config[0] - // Enable colors if no custom format or output is given - if validCustomFormat(cfg.Format) && cfg.Output == nil { - cfg.enableColors = true - } - // Set default values if cfg.Next == nil { cfg.Next = ConfigDefault.Next @@ -118,6 +109,7 @@ func configDefault(config ...Config) Config { if cfg.Format == "" { cfg.Format = ConfigDefault.Format } + if cfg.TimeZone == "" { cfg.TimeZone = ConfigDefault.TimeZone } @@ -130,152 +122,11 @@ func configDefault(config ...Config) Config { if cfg.Output == nil { cfg.Output = ConfigDefault.Output } - // Set custom tags - cfg.tagFunctions = 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 s := c.Context().UserValue("loggerChainError").(string); s != "" { - return buf.WriteString(s) - } - return buf.WriteString("-") - }, - TagReqHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { - return buf.WriteString(c.Get(tag[10:])) - }, - TagHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { - return buf.WriteString(c.Get(tag[7:])) - }, - TagRespHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { - return buf.WriteString(c.GetRespHeader(tag[11:])) - }, - TagQuery: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { - return buf.WriteString(c.Query(tag[6:])) - }, - TagForm: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { - return buf.WriteString(c.FormValue(tag[5:])) - }, - TagCookie: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { - return buf.WriteString(c.Cookies(tag[7:])) - }, - TagLocals: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { - switch v := c.Locals(tag[7:]).(type) { - case []byte: - return buf.Write(v) - case string: - return buf.WriteString(v) - case nil: - return 0, nil - default: - return buf.WriteString(fmt.Sprintf("%v", v)) - } - }, - 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()) - }, - 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()) - }, - TagPid: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { - return buf.WriteString(pid) - }, - TagLatency: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { - ctx := c.Context() - latency := ctx.UserValue(loggerStop).(time.Time).Sub(ctx.UserValue(loggerStart).(time.Time)) - return buf.WriteString(fmt.Sprintf("%7v", latency)) - }, - TagTime: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, w io.Writer, tag string) (int, error) { - return buf.WriteString(timestamp.Load().(string)) - }, - } - for k, v := range cfg.CustomTags { - cfg.tagFunctions[k] = v + // Enable colors if no custom format or output is given + if cfg.Output == nil && checkColorEnable(cfg.Format) { + cfg.enableColors = true } + return cfg } diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index f2f32fc0d7..7089740a5d 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -2,7 +2,6 @@ package logger import ( "fmt" - "io" "os" "strconv" "strings" @@ -11,14 +10,12 @@ import ( "time" "github.com/gofiber/fiber/v2/utils" - "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "github.com/valyala/fasthttp" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/internal/bytebufferpool" - "github.com/gofiber/fiber/v2/internal/fasttemplate" ) // Logger variables @@ -88,9 +85,6 @@ func New(config ...Config) fiber.Handler { // Check if format contains latency cfg.enableLatency = strings.Contains(cfg.Format, "${latency}") - // Create template parser - tmpl := fasttemplate.New(cfg.Format, "${", "}") - // Create correct timeformat timestamp.Store(time.Now().In(cfg.timeZoneLocation).Format(cfg.TimeFormat)) @@ -124,7 +118,10 @@ func New(config ...Config) fiber.Handler { errPadding := 15 errPaddingStr := strconv.Itoa(errPadding) - tagFunctionMap := cfg.tagFunctions + templateChain, logFunChain, err := buildLogFuncChain(&cfg, createTagMap(&cfg)) + if err != nil { + panic(err) + } // Return new handler return func(c *fiber.Ctx) (err error) { @@ -212,19 +209,19 @@ func New(config ...Config) fiber.Handler { } // Loop over template tags to replace it with the correct value - _, err = tmpl.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) { - if logFunc, ok := tagFunctionMap[tag]; ok { - return logFunc(buf, c, w, tag) + for i, logFunc := range logFunChain { + if logFunc == nil { + _, _ = buf.Write(templateChain[i]) + } else if templateChain[i] == nil { + _, err = logFunc(buf, c) + } else { + _, err = logFunc(buf, c, utils.UnsafeString(templateChain[i])) } - - if index := strings.Index(tag, ":"); index != -1 { - if logFunc, ok := tagFunctionMap[tag[0:index+1]]; ok { - return logFunc(buf, c, w, tag) - } + if err != nil { + break } + } - return 0, nil - }) // Also write errors to the buffer if err != nil { _, _ = buf.WriteString(err.Error()) @@ -254,32 +251,3 @@ func appendInt(buf *bytebufferpool.ByteBuffer, v int) (int, error) { buf.B = fasthttp.AppendUint(buf.B, v) return len(buf.B) - old, nil } - -func subStr(str string, start int, length int) (result string) { - s := utils.UnsafeBytes(str) - total := len(s) - if total == 0 { - return - } - // 允许从尾部开始计算 - if start < 0 { - start = total + start - if start < 0 { - return - } - } - if start > total { - return - } - if length < 0 { - length = total - } - - end := start + length - if end > total { - result = utils.UnsafeString(s[start:]) - } else { - result = utils.UnsafeString(s[start:end]) - } - return -} diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 0950bb8f3c..4aa5d733a1 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -396,7 +396,7 @@ func Test_CustomTags(t *testing.T) { 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) { + "custom_tag": func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { return buf.WriteString(customTag) }, }, diff --git a/middleware/logger/tags.go b/middleware/logger/tags.go new file mode 100644 index 0000000000..ab249e07fb --- /dev/null +++ b/middleware/logger/tags.go @@ -0,0 +1,165 @@ +package logger + +import ( + "fmt" + "strings" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/internal/bytebufferpool" +) + +func createTagMap(cfg *Config) map[string]LogFunc { + + // Set custom tags + tagFunctions := map[string]LogFunc{ + TagReferer: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.Get(fiber.HeaderReferer)) + }, + TagProtocol: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.Protocol()) + }, + TagPort: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.Port()) + }, + TagIP: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.IP()) + }, + TagIPs: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.Get(fiber.HeaderXForwardedFor)) + }, + TagHost: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.Hostname()) + }, + TagPath: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.Path()) + }, + TagURL: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.OriginalURL()) + }, + TagUA: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.Get(fiber.HeaderUserAgent)) + }, + TagBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.Write(c.Body()) + }, + TagBytesReceived: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return appendInt(buf, len(c.Request().Body())) + }, + TagBytesSent: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return appendInt(buf, len(c.Response().Body())) + }, + TagRoute: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.Route().Path) + }, + TagResBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.Write(c.Response().Body()) + }, + TagReqHeaders: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...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, params ...string) (int, error) { + return buf.WriteString(c.Request().URI().QueryArgs().String()) + }, + + TagBlack: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Black) + }, + TagRed: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Red) + }, + TagGreen: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Green) + }, + TagYellow: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Yellow) + }, + TagBlue: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Blue) + }, + TagMagenta: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Magenta) + }, + TagCyan: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Cyan) + }, + TagWhite: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.White) + }, + TagReset: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Reset) + }, + TagError: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + if s := c.Context().UserValue("loggerChainError").(string); s != "" { + return buf.WriteString(s) + } + return buf.WriteString("-") + }, + TagReqHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.Get(params[0])) + }, + TagHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.Get(params[0])) + }, + TagRespHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.GetRespHeader(params[0])) + }, + TagQuery: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.Query(params[0])) + }, + TagForm: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.FormValue(params[0])) + }, + TagCookie: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(c.Cookies(params[0])) + }, + TagLocals: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + switch v := c.Locals(params[0]).(type) { + case []byte: + return buf.Write(v) + case string: + return buf.WriteString(v) + case nil: + return 0, nil + default: + return buf.WriteString(fmt.Sprintf("%v", v)) + } + }, + TagStatus: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + if cfg.enableColors { + colors := c.App().Config().ColorScheme + return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset)) + } + return appendInt(buf, c.Response().StatusCode()) + }, + TagMethod: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + if cfg.enableColors { + colors := c.App().Config().ColorScheme + return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset)) + } + return buf.WriteString(c.Method()) + }, + TagPid: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(pid) + }, + TagLatency: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + ctx := c.Context() + latency := ctx.UserValue(loggerStop).(time.Time).Sub(ctx.UserValue(loggerStart).(time.Time)) + return buf.WriteString(fmt.Sprintf("%7v", latency)) + }, + TagTime: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + return buf.WriteString(timestamp.Load().(string)) + }, + } + if cfg.CustomTags != nil { + for k, v := range cfg.CustomTags { + tagFunctions[k] = v + } + } + + return tagFunctions +} diff --git a/middleware/logger/templateChain.go b/middleware/logger/templateChain.go new file mode 100644 index 0000000000..94219d18e1 --- /dev/null +++ b/middleware/logger/templateChain.go @@ -0,0 +1,50 @@ +package logger + +import ( + "bytes" + "errors" + + "github.com/gofiber/fiber/v2/utils" +) + +func buildLogFuncChain(cfg *Config, tagFunctions map[string]LogFunc) (templateChain [][]byte, logChain []LogFunc, err error) { + templateB := utils.UnsafeBytes(cfg.Format) + startTagB := utils.UnsafeBytes(startTag) + endTagB := utils.UnsafeBytes(endTag) + paramSeparatorB := utils.UnsafeBytes(paramSeparator) + + for { + currentPos := bytes.Index(templateB, startTagB) + if currentPos < 0 { + break + } + logChain = append(logChain, nil) + templateChain = append(templateChain, templateB[:currentPos]) + + templateB = templateB[currentPos+len(startTagB):] + currentPos = bytes.Index(templateB, endTagB) + if currentPos < 0 { + // cannot find end tag - just write it to the output. + logChain = append(logChain, nil) + templateChain = append(templateChain, startTagB) + break + } + // first check for tags with parameters + if index := bytes.Index(templateB[:currentPos], paramSeparatorB); index != -1 { + if logFunc, ok := tagFunctions[utils.UnsafeString(templateB[:index+1])]; ok { + logChain = append(logChain, logFunc) + templateChain = append(templateChain, templateB[index+1:currentPos]) + } else { + return nil, nil, errors.New("No parameter found in \"" + utils.UnsafeString(templateB[:currentPos]) + "\"") + } + } else if logFunc, ok := tagFunctions[utils.UnsafeString(templateB[:currentPos])]; ok { + logChain = append(logChain, logFunc) + templateChain = append(templateChain, nil) + } + templateB = templateB[currentPos+len(endTagB):] + } + logChain = append(logChain, nil) + templateChain = append(templateChain, templateB) + + return +} From 04eb75ab93784b0cf12b8e569b9496f4adac0f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Thu, 17 Nov 2022 22:37:17 +0100 Subject: [PATCH 4/6] improve logger performance --- .github/README.md | 1 - .github/README_ckb.md | 1 - .github/README_de.md | 1 - .github/README_es.md | 1 - .github/README_fa.md | 1 - .github/README_fr.md | 1 - .github/README_he.md | 1 - .github/README_id.md | 1 - .github/README_it.md | 1 - .github/README_ja.md | 1 - .github/README_ko.md | 1 - .github/README_nl.md | 1 - .github/README_pt.md | 1 - .github/README_ru.md | 1 - .github/README_sa.md | 1 - .github/README_tr.md | 1 - .github/README_zh-CN.md | 1 - .github/README_zh-TW.md | 1 - internal/fasttemplate/LICENSE | 22 -- internal/fasttemplate/template.go | 437 ---------------------------- internal/fasttemplate/unsafe.go | 22 -- internal/fasttemplate/unsafe_gae.go | 12 - middleware/logger/config.go | 2 +- middleware/logger/logger.go | 11 +- middleware/logger/logger_test.go | 3 +- middleware/logger/tags.go | 97 +++--- middleware/logger/templateChain.go | 39 ++- 27 files changed, 87 insertions(+), 576 deletions(-) delete mode 100644 internal/fasttemplate/LICENSE delete mode 100644 internal/fasttemplate/template.go delete mode 100644 internal/fasttemplate/unsafe.go delete mode 100644 internal/fasttemplate/unsafe_gae.go diff --git a/.github/README.md b/.github/README.md index 43f675f1a0..acced73074 100644 --- a/.github/README.md +++ b/.github/README.md @@ -692,7 +692,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_ckb.md b/.github/README_ckb.md index c62c5d1230..9c5841340a 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -691,7 +691,6 @@ For more articles, middlewares, examples or tools check our [awesome list](https - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_de.md b/.github/README_de.md index aca4a2faeb..c3c33942bd 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -661,7 +661,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_es.md b/.github/README_es.md index 0d3d36a230..918207b59f 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -661,7 +661,6 @@ Copyright (c) 2019-presente [Fenny](https://github.com/fenny) y [contribuyentes] - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_fa.md b/.github/README_fa.md index 7fe0475fa7..5cbaba189f 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -813,7 +813,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_fr.md b/.github/README_fr.md index 8ced8bc23e..4ecf6da868 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -663,7 +663,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_he.md b/.github/README_he.md index 3a8a7c6780..5457e55eb3 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -838,7 +838,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_id.md b/.github/README_id.md index 4d4fc3f7be..98f5fdf409 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -664,7 +664,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_it.md b/.github/README_it.md index b3df35e5da..5f8b06c0b0 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -687,7 +687,6 @@ Copyright (c) 2019-ora [Fenny](https://github.com/fenny) e [Contributors](https: - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_ja.md b/.github/README_ja.md index 29e3f3a15f..4264f83736 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -666,7 +666,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_ko.md b/.github/README_ko.md index c2479baee1..ca2008d55d 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -667,7 +667,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_nl.md b/.github/README_nl.md index 854e360c66..85a7888f11 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -667,7 +667,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_pt.md b/.github/README_pt.md index 52df5a58ac..4568b145d3 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -663,7 +663,6 @@ O logo oficial foi criado por [Vic Shóstak](https://github.com/koddr) e distrib - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_ru.md b/.github/README_ru.md index dad365ad20..a87dcb94a4 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -670,7 +670,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_sa.md b/.github/README_sa.md index efc80c4bfe..740c754e20 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -732,7 +732,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_tr.md b/.github/README_tr.md index c425d40c11..4e4864c786 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -660,7 +660,6 @@ Telif (c) 2019-günümüz [Fenny](https://github.com/fenny) ve [katkıda bulunan - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index 2a64bd04fd..0936f09f2f 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -670,7 +670,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index c1999289aa..68d16bdd23 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -663,7 +663,6 @@ Fiber 是一個以贊助維生的開源專案,像是: 網域、gitbook、netli - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/internal/fasttemplate/LICENSE b/internal/fasttemplate/LICENSE deleted file mode 100644 index 7125a63c4c..0000000000 --- a/internal/fasttemplate/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Aliaksandr Valialkin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/internal/fasttemplate/template.go b/internal/fasttemplate/template.go deleted file mode 100644 index d4559feef8..0000000000 --- a/internal/fasttemplate/template.go +++ /dev/null @@ -1,437 +0,0 @@ -// Package fasttemplate implements simple and fast template library. -// -// Fasttemplate is faster than text/template, strings.Replace -// and strings.Replacer. -// -// Fasttemplate ideally fits for fast and simple placeholders' substitutions. -package fasttemplate - -import ( - "bytes" - "fmt" - "io" - - "github.com/gofiber/fiber/v2/internal/bytebufferpool" -) - -// ExecuteFunc calls f on each template tag (placeholder) occurrence. -// -// Returns the number of bytes written to w. -// -// This function is optimized for constantly changing templates. -// Use Template.ExecuteFunc for frozen templates. -func ExecuteFunc(template, startTag, endTag string, w io.Writer, f TagFunc) (int64, error) { - s := unsafeString2Bytes(template) - a := unsafeString2Bytes(startTag) - b := unsafeString2Bytes(endTag) - - var nn int64 - var ni int - var err error - for { - n := bytes.Index(s, a) - if n < 0 { - break - } - ni, err = w.Write(s[:n]) - nn += int64(ni) - if err != nil { - return nn, err - } - - s = s[n+len(a):] - n = bytes.Index(s, b) - if n < 0 { - // cannot find end tag - just write it to the output. - ni, _ = w.Write(a) - nn += int64(ni) - break - } - - ni, err = f(w, unsafeBytes2String(s[:n])) - nn += int64(ni) - if err != nil { - return nn, err - } - s = s[n+len(b):] - } - ni, err = w.Write(s) - nn += int64(ni) - - return nn, err -} - -// Execute substitutes template tags (placeholders) with the corresponding -// values from the map m and writes the result to the given writer w. -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// Returns the number of bytes written to w. -// -// This function is optimized for constantly changing templates. -// Use Template.Execute for frozen templates. -func Execute(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error) { - return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) }) -} - -// ExecuteStd works the same way as Execute, but keeps the unknown placeholders. -// This can be used as a drop-in replacement for strings.Replacer -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// Returns the number of bytes written to w. -// -// This function is optimized for constantly changing templates. -// Use Template.ExecuteStd for frozen templates. -func ExecuteStd(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error) { - return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, startTag, endTag, tag, m) }) -} - -// ExecuteFuncString calls f on each template tag (placeholder) occurrence -// and substitutes it with the data written to TagFunc's w. -// -// Returns the resulting string. -// -// This function is optimized for constantly changing templates. -// Use Template.ExecuteFuncString for frozen templates. -func ExecuteFuncString(template, startTag, endTag string, f TagFunc) string { - s, err := ExecuteFuncStringWithErr(template, startTag, endTag, f) - if err != nil { - panic(fmt.Sprintf("unexpected error: %s", err)) - } - return s -} - -// ExecuteFuncStringWithErr is nearly the same as ExecuteFuncString -// but when f returns an error, ExecuteFuncStringWithErr won't panic like ExecuteFuncString -// it just returns an empty string and the error f returned -func ExecuteFuncStringWithErr(template, startTag, endTag string, f TagFunc) (string, error) { - tagsCount := bytes.Count(unsafeString2Bytes(template), unsafeString2Bytes(startTag)) - if tagsCount == 0 { - return template, nil - } - - bb := byteBufferPool.Get() - if _, err := ExecuteFunc(template, startTag, endTag, bb, f); err != nil { - bb.Reset() - byteBufferPool.Put(bb) - return "", err - } - s := string(bb.B) - bb.Reset() - byteBufferPool.Put(bb) - return s, nil -} - -var byteBufferPool bytebufferpool.Pool - -// ExecuteString substitutes template tags (placeholders) with the corresponding -// values from the map m and returns the result. -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// This function is optimized for constantly changing templates. -// Use Template.ExecuteString for frozen templates. -func ExecuteString(template, startTag, endTag string, m map[string]interface{}) string { - return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) }) -} - -// ExecuteStringStd works the same way as ExecuteString, but keeps the unknown placeholders. -// This can be used as a drop-in replacement for strings.Replacer -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// This function is optimized for constantly changing templates. -// Use Template.ExecuteStringStd for frozen templates. -func ExecuteStringStd(template, startTag, endTag string, m map[string]interface{}) string { - return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, startTag, endTag, tag, m) }) -} - -// Template implements simple template engine, which can be used for fast -// tags' (aka placeholders) substitution. -type Template struct { - template string - startTag string - endTag string - - texts [][]byte - tags []string - byteBufferPool bytebufferpool.Pool -} - -// New parses the given template using the given startTag and endTag -// as tag start and tag end. -// -// The returned template can be executed by concurrently running goroutines -// using Execute* methods. -// -// New panics if the given template cannot be parsed. Use NewTemplate instead -// if template may contain errors. -func New(template, startTag, endTag string) *Template { - t, err := NewTemplate(template, startTag, endTag) - if err != nil { - panic(err) - } - return t -} - -// NewTemplate parses the given template using the given startTag and endTag -// as tag start and tag end. -// -// The returned template can be executed by concurrently running goroutines -// using Execute* methods. -func NewTemplate(template, startTag, endTag string) (*Template, error) { - var t Template - err := t.Reset(template, startTag, endTag) - if err != nil { - return nil, err - } - return &t, nil -} - -// TagFunc can be used as a substitution value in the map passed to Execute*. -// Execute* functions pass tag (placeholder) name in 'tag' argument. -// -// TagFunc must be safe to call from concurrently running goroutines. -// -// TagFunc must write contents to w and return the number of bytes written. -type TagFunc func(w io.Writer, tag string) (int, error) - -// Reset resets the template t to new one defined by -// template, startTag and endTag. -// -// Reset allows Template object re-use. -// -// Reset may be called only if no other goroutines call t methods at the moment. -func (t *Template) Reset(template, startTag, endTag string) error { - // Keep these vars in t, so GC won't collect them and won't break - // vars derived via unsafe* - t.template = template - t.startTag = startTag - t.endTag = endTag - t.texts = t.texts[:0] - t.tags = t.tags[:0] - - if len(startTag) == 0 { - panic("startTag cannot be empty") - } - if len(endTag) == 0 { - panic("endTag cannot be empty") - } - - s := unsafeString2Bytes(template) - a := unsafeString2Bytes(startTag) - b := unsafeString2Bytes(endTag) - - tagsCount := bytes.Count(s, a) - if tagsCount == 0 { - return nil - } - - if tagsCount+1 > cap(t.texts) { - t.texts = make([][]byte, 0, tagsCount+1) - } - if tagsCount > cap(t.tags) { - t.tags = make([]string, 0, tagsCount) - } - - for { - n := bytes.Index(s, a) - if n < 0 { - t.texts = append(t.texts, s) - break - } - t.texts = append(t.texts, s[:n]) - - s = s[n+len(a):] - n = bytes.Index(s, b) - if n < 0 { - return fmt.Errorf("cannot find end tag=%q in the template=%q starting from %q", endTag, template, s) - } - - t.tags = append(t.tags, unsafeBytes2String(s[:n])) - s = s[n+len(b):] - } - - return nil -} - -// ExecuteFunc calls f on each template tag (placeholder) occurrence. -// -// Returns the number of bytes written to w. -// -// This function is optimized for frozen templates. -// Use ExecuteFunc for constantly changing templates. -func (t *Template) ExecuteFunc(w io.Writer, f TagFunc) (int64, error) { - var nn int64 - - n := len(t.texts) - 1 - if n == -1 { - ni, err := w.Write(unsafeString2Bytes(t.template)) - return int64(ni), err - } - - for i := 0; i < n; i++ { - ni, err := w.Write(t.texts[i]) - nn += int64(ni) - if err != nil { - return nn, err - } - - ni, err = f(w, t.tags[i]) - nn += int64(ni) - if err != nil { - return nn, err - } - } - ni, err := w.Write(t.texts[n]) - nn += int64(ni) - return nn, err -} - -// Execute substitutes template tags (placeholders) with the corresponding -// values from the map m and writes the result to the given writer w. -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// Returns the number of bytes written to w. -func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error) { - return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) }) -} - -// ExecuteStd works the same way as Execute, but keeps the unknown placeholders. -// This can be used as a drop-in replacement for strings.Replacer -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// Returns the number of bytes written to w. -func (t *Template) ExecuteStd(w io.Writer, m map[string]interface{}) (int64, error) { - return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, t.startTag, t.endTag, tag, m) }) -} - -// ExecuteFuncString calls f on each template tag (placeholder) occurrence -// and substitutes it with the data written to TagFunc's w. -// -// Returns the resulting string. -// -// This function is optimized for frozen templates. -// Use ExecuteFuncString for constantly changing templates. -func (t *Template) ExecuteFuncString(f TagFunc) string { - s, err := t.ExecuteFuncStringWithErr(f) - if err != nil { - panic(fmt.Sprintf("unexpected error: %s", err)) - } - return s -} - -// ExecuteFuncStringWithErr calls f on each template tag (placeholder) occurrence -// and substitutes it with the data written to TagFunc's w. -// -// Returns the resulting string. -// -// This function is optimized for frozen templates. -// Use ExecuteFuncString for constantly changing templates. -func (t *Template) ExecuteFuncStringWithErr(f TagFunc) (string, error) { - bb := t.byteBufferPool.Get() - if _, err := t.ExecuteFunc(bb, f); err != nil { - bb.Reset() - t.byteBufferPool.Put(bb) - return "", err - } - s := string(bb.Bytes()) - bb.Reset() - t.byteBufferPool.Put(bb) - return s, nil -} - -// ExecuteString substitutes template tags (placeholders) with the corresponding -// values from the map m and returns the result. -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// This function is optimized for frozen templates. -// Use ExecuteString for constantly changing templates. -func (t *Template) ExecuteString(m map[string]interface{}) string { - return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) }) -} - -// ExecuteStringStd works the same way as ExecuteString, but keeps the unknown placeholders. -// This can be used as a drop-in replacement for strings.Replacer -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// This function is optimized for frozen templates. -// Use ExecuteStringStd for constantly changing templates. -func (t *Template) ExecuteStringStd(m map[string]interface{}) string { - return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, t.startTag, t.endTag, tag, m) }) -} - -func stdTagFunc(w io.Writer, tag string, m map[string]interface{}) (int, error) { - v := m[tag] - if v == nil { - return 0, nil - } - switch value := v.(type) { - case []byte: - return w.Write(value) - case string: - return w.Write([]byte(value)) - case TagFunc: - return value(w, tag) - default: - panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v)) - } -} - -func keepUnknownTagFunc(w io.Writer, startTag, endTag, tag string, m map[string]interface{}) (int, error) { - v, ok := m[tag] - if !ok { - if _, err := w.Write(unsafeString2Bytes(startTag)); err != nil { - return 0, err - } - if _, err := w.Write(unsafeString2Bytes(tag)); err != nil { - return 0, err - } - if _, err := w.Write(unsafeString2Bytes(endTag)); err != nil { - return 0, err - } - return len(startTag) + len(tag) + len(endTag), nil - } - if v == nil { - return 0, nil - } - switch value := v.(type) { - case []byte: - return w.Write(value) - case string: - return w.Write([]byte(value)) - case TagFunc: - return value(w, tag) - default: - panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v)) - } -} diff --git a/internal/fasttemplate/unsafe.go b/internal/fasttemplate/unsafe.go deleted file mode 100644 index 1d0bc9e857..0000000000 --- a/internal/fasttemplate/unsafe.go +++ /dev/null @@ -1,22 +0,0 @@ -//go:build !appengine -// +build !appengine - -package fasttemplate - -import ( - "reflect" - "unsafe" -) - -func unsafeBytes2String(b []byte) string { - return *(*string)(unsafe.Pointer(&b)) -} - -func unsafeString2Bytes(s string) (b []byte) { - sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) - bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) - bh.Data = sh.Data - bh.Cap = sh.Len - bh.Len = sh.Len - return b -} diff --git a/internal/fasttemplate/unsafe_gae.go b/internal/fasttemplate/unsafe_gae.go deleted file mode 100644 index dbfed3bac4..0000000000 --- a/internal/fasttemplate/unsafe_gae.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build appengine -// +build appengine - -package fasttemplate - -func unsafeBytes2String(b []byte) string { - return string(b) -} - -func unsafeString2Bytes(s string) []byte { - return []byte(s) -} diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 13b1a44e74..8d377cd345 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -64,7 +64,7 @@ const ( paramSeparator = ":" ) -type LogFunc func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) +type LogFunc func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) // ConfigDefault is the default config var ConfigDefault = Config{ diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 7089740a5d..f5bd8f5512 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -83,13 +83,13 @@ func New(config ...Config) fiber.Handler { } // Check if format contains latency - cfg.enableLatency = strings.Contains(cfg.Format, "${latency}") + cfg.enableLatency = strings.Contains(cfg.Format, "${"+TagLatency+"}") // Create correct timeformat timestamp.Store(time.Now().In(cfg.timeZoneLocation).Format(cfg.TimeFormat)) // Update date/time every 500 milliseconds in a separate go routine - if strings.Contains(cfg.Format, "${time}") { + if strings.Contains(cfg.Format, "${"+TagTime+"}") { go func() { for { time.Sleep(cfg.TimeInterval) @@ -118,6 +118,8 @@ func New(config ...Config) fiber.Handler { errPadding := 15 errPaddingStr := strconv.Itoa(errPadding) + // instead of analyzing the template inside(handler) each time, this is done once before + // and we create several slices of the same length with the functions to be executed and fixed parts. templateChain, logFunChain, err := buildLogFuncChain(&cfg, createTagMap(&cfg)) if err != nil { panic(err) @@ -154,6 +156,7 @@ func New(config ...Config) fiber.Handler { // Set latency start time if cfg.enableLatency { start = time.Now() + // TODO: optimize it with a data structure and sync pool c.Context().SetUserValue(loggerStart, start) } @@ -208,12 +211,12 @@ func New(config ...Config) fiber.Handler { return nil } - // Loop over template tags to replace it with the correct value + // Loop over template parts execute dynamic parts and add fixed parts to the buffer for i, logFunc := range logFunChain { if logFunc == nil { _, _ = buf.Write(templateChain[i]) } else if templateChain[i] == nil { - _, err = logFunc(buf, c) + _, err = logFunc(buf, c, "") } else { _, err = logFunc(buf, c, utils.UnsafeString(templateChain[i])) } diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 4aa5d733a1..cfe970273b 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -291,6 +291,7 @@ func Benchmark_Logger(b *testing.B) { app.Use(New(Config{ Format: "${bytesReceived} ${bytesSent} ${status} ${reqHeader:test}", + //Format: "[${time}] ${status} - ${latency} ${method} ${path}\n",// <- produces allocations Output: io.Discard, })) app.Get("/", func(c *fiber.Ctx) error { @@ -396,7 +397,7 @@ func Test_CustomTags(t *testing.T) { app.Use(New(Config{ Format: "${custom_tag}", CustomTags: map[string]LogFunc{ - "custom_tag": func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + "custom_tag": func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(customTag) }, }, diff --git a/middleware/logger/tags.go b/middleware/logger/tags.go index ab249e07fb..d39883e2d3 100644 --- a/middleware/logger/tags.go +++ b/middleware/logger/tags.go @@ -9,116 +9,116 @@ import ( "github.com/gofiber/fiber/v2/internal/bytebufferpool" ) +// createTagMap function merged the default with the custom tags func createTagMap(cfg *Config) map[string]LogFunc { - - // Set custom tags + // Set default tags tagFunctions := map[string]LogFunc{ - TagReferer: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagReferer: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.Get(fiber.HeaderReferer)) }, - TagProtocol: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagProtocol: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.Protocol()) }, - TagPort: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagPort: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.Port()) }, - TagIP: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagIP: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.IP()) }, - TagIPs: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagIPs: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.Get(fiber.HeaderXForwardedFor)) }, - TagHost: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagHost: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.Hostname()) }, - TagPath: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagPath: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.Path()) }, - TagURL: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagURL: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.OriginalURL()) }, - TagUA: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagUA: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.Get(fiber.HeaderUserAgent)) }, - TagBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.Write(c.Body()) }, - TagBytesReceived: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagBytesReceived: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return appendInt(buf, len(c.Request().Body())) }, - TagBytesSent: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagBytesSent: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return appendInt(buf, len(c.Response().Body())) }, - TagRoute: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagRoute: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.Route().Path) }, - TagResBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagResBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.Write(c.Response().Body()) }, - TagReqHeaders: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagReqHeaders: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam 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, params ...string) (int, error) { + TagQueryStringParams: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.Request().URI().QueryArgs().String()) }, - TagBlack: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagBlack: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Black) }, - TagRed: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagRed: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Red) }, - TagGreen: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagGreen: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Green) }, - TagYellow: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagYellow: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Yellow) }, - TagBlue: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagBlue: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Blue) }, - TagMagenta: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagMagenta: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Magenta) }, - TagCyan: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagCyan: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Cyan) }, - TagWhite: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagWhite: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.White) }, - TagReset: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagReset: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Reset) }, - TagError: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagError: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { if s := c.Context().UserValue("loggerChainError").(string); s != "" { return buf.WriteString(s) } return buf.WriteString("-") }, - TagReqHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { - return buf.WriteString(c.Get(params[0])) + TagReqHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + return buf.WriteString(c.Get(extraParam)) }, - TagHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { - return buf.WriteString(c.Get(params[0])) + TagHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + return buf.WriteString(c.Get(extraParam)) }, - TagRespHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { - return buf.WriteString(c.GetRespHeader(params[0])) + TagRespHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + return buf.WriteString(c.GetRespHeader(extraParam)) }, - TagQuery: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { - return buf.WriteString(c.Query(params[0])) + TagQuery: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + return buf.WriteString(c.Query(extraParam)) }, - TagForm: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { - return buf.WriteString(c.FormValue(params[0])) + TagForm: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + return buf.WriteString(c.FormValue(extraParam)) }, - TagCookie: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { - return buf.WriteString(c.Cookies(params[0])) + TagCookie: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + return buf.WriteString(c.Cookies(extraParam)) }, - TagLocals: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { - switch v := c.Locals(params[0]).(type) { + TagLocals: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + switch v := c.Locals(extraParam).(type) { case []byte: return buf.Write(v) case string: @@ -129,32 +129,33 @@ func createTagMap(cfg *Config) map[string]LogFunc { return buf.WriteString(fmt.Sprintf("%v", v)) } }, - TagStatus: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagStatus: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { if cfg.enableColors { colors := c.App().Config().ColorScheme return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset)) } return appendInt(buf, c.Response().StatusCode()) }, - TagMethod: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagMethod: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { if cfg.enableColors { colors := c.App().Config().ColorScheme return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset)) } return buf.WriteString(c.Method()) }, - TagPid: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagPid: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(pid) }, - TagLatency: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagLatency: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { ctx := c.Context() - latency := ctx.UserValue(loggerStop).(time.Time).Sub(ctx.UserValue(loggerStart).(time.Time)) + latency := ctx.UserValue(loggerStop).(time.Time).Sub(ctx.UserValue(loggerStart).(time.Time)).Round(time.Millisecond) return buf.WriteString(fmt.Sprintf("%7v", latency)) }, - TagTime: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, params ...string) (int, error) { + TagTime: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { return buf.WriteString(timestamp.Load().(string)) }, } + // merge with custom tags from user if cfg.CustomTags != nil { for k, v := range cfg.CustomTags { tagFunctions[k] = v diff --git a/middleware/logger/templateChain.go b/middleware/logger/templateChain.go index 94219d18e1..ceb31a3356 100644 --- a/middleware/logger/templateChain.go +++ b/middleware/logger/templateChain.go @@ -7,7 +7,15 @@ import ( "github.com/gofiber/fiber/v2/utils" ) -func buildLogFuncChain(cfg *Config, tagFunctions map[string]LogFunc) (templateChain [][]byte, logChain []LogFunc, err error) { +// buildLogFuncChain analyzes the template and creates slices with the functions for execution and +// slices with the fixed parts of the template and the parameters +// +// fixParts contains the fixed parts of the template or parameters if a function is stored in the funcChain at this position +// funcChain contains for the parts which exist the functions for the dynamic parts +// funcChain and fixParts always have the same length and contain nil for the parts where no data is required in the chain, +// if a function exists for the part, a parameter for it can also exist in the fixParts slice +func buildLogFuncChain(cfg *Config, tagFunctions map[string]LogFunc) (fixParts [][]byte, funcChain []LogFunc, err error) { + // process flow is copied from the fasttemplate flow https://github.com/valyala/fasttemplate/blob/2a2d1afadadf9715bfa19683cdaeac8347e5d9f9/template.go#L23-L62 templateB := utils.UnsafeBytes(cfg.Format) startTagB := utils.UnsafeBytes(startTag) endTagB := utils.UnsafeBytes(endTag) @@ -16,35 +24,44 @@ func buildLogFuncChain(cfg *Config, tagFunctions map[string]LogFunc) (templateCh for { currentPos := bytes.Index(templateB, startTagB) if currentPos < 0 { + // no starting tag found in the existing template part break } - logChain = append(logChain, nil) - templateChain = append(templateChain, templateB[:currentPos]) + // add fixed part + funcChain = append(funcChain, nil) + fixParts = append(fixParts, templateB[:currentPos]) templateB = templateB[currentPos+len(startTagB):] currentPos = bytes.Index(templateB, endTagB) if currentPos < 0 { // cannot find end tag - just write it to the output. - logChain = append(logChain, nil) - templateChain = append(templateChain, startTagB) + funcChain = append(funcChain, nil) + fixParts = append(fixParts, startTagB) break } + // ## function block ## // first check for tags with parameters if index := bytes.Index(templateB[:currentPos], paramSeparatorB); index != -1 { if logFunc, ok := tagFunctions[utils.UnsafeString(templateB[:index+1])]; ok { - logChain = append(logChain, logFunc) - templateChain = append(templateChain, templateB[index+1:currentPos]) + funcChain = append(funcChain, logFunc) + // add param to the fixParts + fixParts = append(fixParts, templateB[index+1:currentPos]) } else { return nil, nil, errors.New("No parameter found in \"" + utils.UnsafeString(templateB[:currentPos]) + "\"") } } else if logFunc, ok := tagFunctions[utils.UnsafeString(templateB[:currentPos])]; ok { - logChain = append(logChain, logFunc) - templateChain = append(templateChain, nil) + // add functions without parameter + funcChain = append(funcChain, logFunc) + fixParts = append(fixParts, nil) } + // ## function block end ## + + // reduce the template string templateB = templateB[currentPos+len(endTagB):] } - logChain = append(logChain, nil) - templateChain = append(templateChain, templateB) + // set the rest + funcChain = append(funcChain, nil) + fixParts = append(fixParts, templateB) return } From 866e05d194c18e9900c462980ed949fea3e50b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Fri, 18 Nov 2022 11:09:11 +0100 Subject: [PATCH 5/6] improve logger performance --- middleware/logger/README.md | 2 +- middleware/logger/config.go | 2 +- middleware/logger/data.go | 19 +++ middleware/logger/logger.go | 79 +++-------- middleware/logger/logger_test.go | 69 +++++++--- middleware/logger/tags.go | 130 ++++++++++++------ .../{templateChain.go => template_chain.go} | 0 7 files changed, 171 insertions(+), 130 deletions(-) create mode 100644 middleware/logger/data.go rename middleware/logger/{templateChain.go => template_chain.go} (100%) diff --git a/middleware/logger/README.md b/middleware/logger/README.md index 7b69d916f9..f6a7011711 100644 --- a/middleware/logger/README.md +++ b/middleware/logger/README.md @@ -80,7 +80,7 @@ app.Use(logger.New(logger.Config{ ```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) { + "custom_tag": func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString("it is a custom tag") }, }, diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 8d377cd345..685ce618f4 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -64,7 +64,7 @@ const ( paramSeparator = ":" ) -type LogFunc func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) +type LogFunc func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) // ConfigDefault is the default config var ConfigDefault = Config{ diff --git a/middleware/logger/data.go b/middleware/logger/data.go new file mode 100644 index 0000000000..611a0aeee2 --- /dev/null +++ b/middleware/logger/data.go @@ -0,0 +1,19 @@ +package logger + +import ( + "sync" + "sync/atomic" + "time" +) + +var DataPool = sync.Pool{New: func() interface{} { return new(Data) }} + +// Data is a struct to define some variables to use in custom logger function. +type Data struct { + Pid string + ErrPaddingStr string + ChainErr error + Start time.Time + Stop time.Time + Timestamp atomic.Value +} diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index f5bd8f5512..967105883b 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -18,57 +18,6 @@ import ( "github.com/gofiber/fiber/v2/internal/bytebufferpool" ) -// Logger variables -const ( - TagPid = "pid" - TagTime = "time" - TagReferer = "referer" - TagProtocol = "protocol" - TagPort = "port" - TagIP = "ip" - TagIPs = "ips" - TagHost = "host" - TagMethod = "method" - TagPath = "path" - TagURL = "url" - TagUA = "ua" - TagLatency = "latency" - TagStatus = "status" - TagResBody = "resBody" - TagReqHeaders = "reqHeaders" - TagQueryStringParams = "queryParams" - TagBody = "body" - TagBytesSent = "bytesSent" - TagBytesReceived = "bytesReceived" - TagRoute = "route" - TagError = "error" - // DEPRECATED: Use TagReqHeader instead - TagHeader = "header:" - TagReqHeader = "reqHeader:" - TagRespHeader = "respHeader:" - TagLocals = "locals:" - TagQuery = "query:" - TagForm = "form:" - TagCookie = "cookie:" - TagBlack = "black" - TagRed = "red" - TagGreen = "green" - TagYellow = "yellow" - TagBlue = "blue" - TagMagenta = "magenta" - TagCyan = "cyan" - TagWhite = "white" - TagReset = "reset" -) - -const ( - loggerStart = iota - loggerStop -) - -var pid string -var timestamp atomic.Value - // New creates a new middleware handler func New(config ...Config) fiber.Handler { // Set default config @@ -85,6 +34,7 @@ func New(config ...Config) fiber.Handler { // Check if format contains latency cfg.enableLatency = strings.Contains(cfg.Format, "${"+TagLatency+"}") + var timestamp atomic.Value // Create correct timeformat timestamp.Store(time.Now().In(cfg.timeZoneLocation).Format(cfg.TimeFormat)) @@ -99,7 +49,7 @@ func New(config ...Config) fiber.Handler { } // Set PID once - pid = strconv.Itoa(os.Getpid()) + pid := strconv.Itoa(os.Getpid()) // Set variables var ( @@ -127,6 +77,14 @@ func New(config ...Config) fiber.Handler { // Return new handler return func(c *fiber.Ctx) (err error) { + // Logger data + data := DataPool.Get().(*Data) + // no need for a reset, as long as we always override everything + data.Pid = pid + data.ErrPaddingStr = errPaddingStr + data.Timestamp = timestamp + // put data back in the pool + defer DataPool.Put(data) // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() @@ -151,21 +109,17 @@ func New(config ...Config) fiber.Handler { errHandler = c.App().ErrorHandler }) - var start, stop time.Time - // Set latency start time if cfg.enableLatency { - start = time.Now() - // TODO: optimize it with a data structure and sync pool - c.Context().SetUserValue(loggerStart, start) + data.Start = time.Now() } // Handle request, store err for logging chainErr := c.Next() + data.ChainErr = chainErr // Manually call error handler if chainErr != nil { - c.Context().SetUserValue("loggerChainError", chainErr.Error()) if err := errHandler(c, chainErr); err != nil { _ = c.SendStatus(fiber.StatusInternalServerError) } @@ -173,8 +127,7 @@ func New(config ...Config) fiber.Handler { // Set latency stop time if cfg.enableLatency { - stop = time.Now() - c.Context().SetUserValue(loggerStop, stop) + data.Stop = time.Now() } // Get new buffer @@ -192,7 +145,7 @@ func New(config ...Config) fiber.Handler { _, _ = buf.WriteString(fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+errPaddingStr+"s %s\n", timestamp.Load().(string), statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset, - stop.Sub(start).Round(time.Millisecond), + data.Stop.Sub(data.Start).Round(time.Millisecond), c.IP(), methodColor(c.Method(), colors), c.Method(), colors.Reset, c.Path(), @@ -216,9 +169,9 @@ func New(config ...Config) fiber.Handler { if logFunc == nil { _, _ = buf.Write(templateChain[i]) } else if templateChain[i] == nil { - _, err = logFunc(buf, c, "") + _, err = logFunc(buf, c, data, "") } else { - _, err = logFunc(buf, c, utils.UnsafeString(templateChain[i])) + _, err = logFunc(buf, c, data, utils.UnsafeString(templateChain[i])) } if err != nil { break diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index cfe970273b..eae71bb313 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -287,32 +287,59 @@ func Test_Logger_Data_Race(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Logger -benchmem -count=4 func Benchmark_Logger(b *testing.B) { - app := fiber.New() - - app.Use(New(Config{ - Format: "${bytesReceived} ${bytesSent} ${status} ${reqHeader:test}", - //Format: "[${time}] ${status} - ${latency} ${method} ${path}\n",// <- produces allocations - Output: io.Discard, - })) - app.Get("/", func(c *fiber.Ctx) error { - c.Set("test", "test") - return c.SendString("Hello, World!") - }) + benchSetup := func(bb *testing.B, app *fiber.App) { + h := app.Handler() - h := app.Handler() + fctx := &fasthttp.RequestCtx{} + fctx.Request.Header.SetMethod("GET") + fctx.Request.SetRequestURI("/") - fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") - fctx.Request.SetRequestURI("/") + bb.ReportAllocs() + bb.ResetTimer() - b.ReportAllocs() - b.ResetTimer() + for n := 0; n < bb.N; n++ { + h(fctx) + } - for n := 0; n < b.N; n++ { - h(fctx) + utils.AssertEqual(bb, 200, fctx.Response.Header.StatusCode()) } - utils.AssertEqual(b, 200, fctx.Response.Header.StatusCode()) + b.Run("Base", func(bb *testing.B) { + app := fiber.New() + app.Use(New(Config{ + Format: "${bytesReceived} ${bytesSent} ${status}", + Output: io.Discard, + })) + app.Get("/", func(c *fiber.Ctx) error { + c.Set("test", "test") + return c.SendString("Hello, World!") + }) + benchSetup(bb, app) + }) + + b.Run("DefaultFormat", func(bb *testing.B) { + app := fiber.New() + app.Use(New(Config{ + Output: io.Discard, + })) + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + benchSetup(bb, app) + }) + + b.Run("WithTagParameter", func(bb *testing.B) { + app := fiber.New() + app.Use(New(Config{ + Format: "${bytesReceived} ${bytesSent} ${status} ${reqHeader:test}", + Output: io.Discard, + })) + app.Get("/", func(c *fiber.Ctx) error { + c.Set("test", "test") + return c.SendString("Hello, World!") + }) + benchSetup(bb, app) + }) } // go test -run Test_Response_Header @@ -397,7 +424,7 @@ func Test_CustomTags(t *testing.T) { app.Use(New(Config{ Format: "${custom_tag}", CustomTags: map[string]LogFunc{ - "custom_tag": func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + "custom_tag": func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(customTag) }, }, diff --git a/middleware/logger/tags.go b/middleware/logger/tags.go index d39883e2d3..db72c43f4e 100644 --- a/middleware/logger/tags.go +++ b/middleware/logger/tags.go @@ -9,115 +9,158 @@ import ( "github.com/gofiber/fiber/v2/internal/bytebufferpool" ) +// Logger variables +const ( + TagPid = "pid" + TagTime = "time" + TagReferer = "referer" + TagProtocol = "protocol" + TagPort = "port" + TagIP = "ip" + TagIPs = "ips" + TagHost = "host" + TagMethod = "method" + TagPath = "path" + TagURL = "url" + TagUA = "ua" + TagLatency = "latency" + TagStatus = "status" + TagResBody = "resBody" + TagReqHeaders = "reqHeaders" + TagQueryStringParams = "queryParams" + TagBody = "body" + TagBytesSent = "bytesSent" + TagBytesReceived = "bytesReceived" + TagRoute = "route" + TagError = "error" + // DEPRECATED: Use TagReqHeader instead + TagHeader = "header:" + TagReqHeader = "reqHeader:" + TagRespHeader = "respHeader:" + TagLocals = "locals:" + TagQuery = "query:" + TagForm = "form:" + TagCookie = "cookie:" + TagBlack = "black" + TagRed = "red" + TagGreen = "green" + TagYellow = "yellow" + TagBlue = "blue" + TagMagenta = "magenta" + TagCyan = "cyan" + TagWhite = "white" + TagReset = "reset" +) + // createTagMap function merged the default with the custom tags func createTagMap(cfg *Config) map[string]LogFunc { // Set default tags tagFunctions := map[string]LogFunc{ - TagReferer: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagReferer: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.Get(fiber.HeaderReferer)) }, - TagProtocol: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagProtocol: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.Protocol()) }, - TagPort: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagPort: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.Port()) }, - TagIP: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagIP: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.IP()) }, - TagIPs: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagIPs: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.Get(fiber.HeaderXForwardedFor)) }, - TagHost: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagHost: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.Hostname()) }, - TagPath: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagPath: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.Path()) }, - TagURL: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagURL: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.OriginalURL()) }, - TagUA: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagUA: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.Get(fiber.HeaderUserAgent)) }, - TagBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.Write(c.Body()) }, - TagBytesReceived: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagBytesReceived: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return appendInt(buf, len(c.Request().Body())) }, - TagBytesSent: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagBytesSent: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return appendInt(buf, len(c.Response().Body())) }, - TagRoute: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagRoute: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.Route().Path) }, - TagResBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagResBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.Write(c.Response().Body()) }, - TagReqHeaders: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagReqHeaders: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam 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, extraParam string) (int, error) { + TagQueryStringParams: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.Request().URI().QueryArgs().String()) }, - TagBlack: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagBlack: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Black) }, - TagRed: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagRed: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Red) }, - TagGreen: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagGreen: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Green) }, - TagYellow: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagYellow: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Yellow) }, - TagBlue: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagBlue: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Blue) }, - TagMagenta: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagMagenta: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Magenta) }, - TagCyan: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagCyan: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Cyan) }, - TagWhite: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagWhite: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.White) }, - TagReset: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagReset: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.App().Config().ColorScheme.Reset) }, - TagError: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { - if s := c.Context().UserValue("loggerChainError").(string); s != "" { - return buf.WriteString(s) + TagError: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + if data.ChainErr != nil { + return buf.WriteString(data.ChainErr.Error()) } return buf.WriteString("-") }, - TagReqHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagReqHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.Get(extraParam)) }, - TagHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.Get(extraParam)) }, - TagRespHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagRespHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.GetRespHeader(extraParam)) }, - TagQuery: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagQuery: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.Query(extraParam)) }, - TagForm: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagForm: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.FormValue(extraParam)) }, - TagCookie: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagCookie: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { return buf.WriteString(c.Cookies(extraParam)) }, - TagLocals: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagLocals: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { switch v := c.Locals(extraParam).(type) { case []byte: return buf.Write(v) @@ -129,30 +172,29 @@ func createTagMap(cfg *Config) map[string]LogFunc { return buf.WriteString(fmt.Sprintf("%v", v)) } }, - TagStatus: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagStatus: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { if cfg.enableColors { colors := c.App().Config().ColorScheme return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset)) } return appendInt(buf, c.Response().StatusCode()) }, - TagMethod: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { + TagMethod: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { if cfg.enableColors { colors := c.App().Config().ColorScheme return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset)) } return buf.WriteString(c.Method()) }, - TagPid: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { - return buf.WriteString(pid) + TagPid: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(data.Pid) }, - TagLatency: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { - ctx := c.Context() - latency := ctx.UserValue(loggerStop).(time.Time).Sub(ctx.UserValue(loggerStart).(time.Time)).Round(time.Millisecond) + TagLatency: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + latency := data.Stop.Sub(data.Start).Round(time.Millisecond) return buf.WriteString(fmt.Sprintf("%7v", latency)) }, - TagTime: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, extraParam string) (int, error) { - return buf.WriteString(timestamp.Load().(string)) + TagTime: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(data.Timestamp.Load().(string)) }, } // merge with custom tags from user diff --git a/middleware/logger/templateChain.go b/middleware/logger/template_chain.go similarity index 100% rename from middleware/logger/templateChain.go rename to middleware/logger/template_chain.go From 516ef4722884f61097928c1d67c95e8705e354c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Fri, 18 Nov 2022 12:44:08 +0100 Subject: [PATCH 6/6] improve logger performance --- middleware/logger/logger.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 967105883b..41d2564a04 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -9,12 +9,12 @@ import ( "sync/atomic" "time" + "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "github.com/valyala/fasthttp" - "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/internal/bytebufferpool" ) @@ -77,14 +77,6 @@ func New(config ...Config) fiber.Handler { // Return new handler return func(c *fiber.Ctx) (err error) { - // Logger data - data := DataPool.Get().(*Data) - // no need for a reset, as long as we always override everything - data.Pid = pid - data.ErrPaddingStr = errPaddingStr - data.Timestamp = timestamp - // put data back in the pool - defer DataPool.Put(data) // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() @@ -109,6 +101,15 @@ func New(config ...Config) fiber.Handler { errHandler = c.App().ErrorHandler }) + // Logger data + data := DataPool.Get().(*Data) + // no need for a reset, as long as we always override everything + data.Pid = pid + data.ErrPaddingStr = errPaddingStr + data.Timestamp = timestamp + // put data back in the pool + defer DataPool.Put(data) + // Set latency start time if cfg.enableLatency { data.Start = time.Now()