From 3689da6a1b424f7b4e10f43306c538b05493b16e Mon Sep 17 00:00:00 2001 From: Hao Chun Chang Date: Sat, 5 Oct 2024 09:30:58 +0800 Subject: [PATCH 01/21] :fire: Feature: Add SetFlags to Logger Interface :fire: Feature: Add fiberlog Logger field to config --- log/default.go | 4 ++++ log/log.go | 1 + middleware/logger/config.go | 3 +++ 3 files changed, 8 insertions(+) diff --git a/log/default.go b/log/default.go index 9a3c93b1c2..86287059fa 100644 --- a/log/default.go +++ b/log/default.go @@ -210,6 +210,10 @@ func (l *defaultLogger) SetOutput(writer io.Writer) { l.stdlog.SetOutput(writer) } +func (l *defaultLogger) SetFlags(flag int) { + l.stdlog.SetFlags(flag) +} + // DefaultLogger returns the default logger. func DefaultLogger() AllLogger { return logger diff --git a/log/log.go b/log/log.go index 9d9cd8b0d2..5fac67e035 100644 --- a/log/log.go +++ b/log/log.go @@ -56,6 +56,7 @@ type CommonLogger interface { type ControlLogger interface { SetLevel(level Level) SetOutput(w io.Writer) + SetFlags(flags int) } // AllLogger is the combination of Logger, FormatLogger, CtxLogger and ControlLogger. diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 4826151e46..1b3799048c 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -6,6 +6,7 @@ import ( "time" "github.com/gofiber/fiber/v3" + fiberlog "github.com/gofiber/fiber/v3/log" ) // Config defines the config for middleware. @@ -43,6 +44,8 @@ type Config struct { // Optional. Default: defaultLogger LoggerFunc func(c fiber.Ctx, data *Data, cfg Config) error + Logger fiberlog.AllLogger + timeZoneLocation *time.Location // Format defines the logging tags From a32b80a63bb52229097859a6755dad0861b84484 Mon Sep 17 00:00:00 2001 From: Hao Chun Chang Date: Sat, 5 Oct 2024 09:38:25 +0800 Subject: [PATCH 02/21] :rotating_light: Test: custom-defined Logger and LoggerFunc --- middleware/logger/logger_test.go | 57 ++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 946db05d80..5215da9dd1 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -15,6 +15,7 @@ import ( "time" "github.com/gofiber/fiber/v3" + fiberlog "github.com/gofiber/fiber/v3/log" "github.com/gofiber/fiber/v3/middleware/requestid" "github.com/stretchr/testify/require" "github.com/valyala/bytebufferpool" @@ -181,6 +182,37 @@ func Test_Logger_ErrorTimeZone(t *testing.T) { require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } +// go test -run Test_Logger_Fiber_Logger +func Test_Logger_Fiber_Logger(t *testing.T) { + t.Parallel() + app := fiber.New() + + buf := bytebufferpool.Get() + defer bytebufferpool.Put(buf) + + customLoggerFunc := func(c fiber.Ctx, data *Data, cfg Config) error { + cfg.Logger.SetOutput(cfg.Output) + cfg.Logger.SetFlags(0) + cfg.Logger.Error(data.ChainErr.Error()) + return nil + } + + app.Use(New(Config{ + Output: buf, + Logger: fiberlog.DefaultLogger(), + LoggerFunc: customLoggerFunc, + })) + + app.Get("/", func(_ fiber.Ctx) error { + return errors.New("some random error") + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + require.NoError(t, err) + require.Equal(t, fiber.StatusInternalServerError, resp.StatusCode) + require.Equal(t, "[Error] some random error\n", buf.String()) +} + type fakeErrorOutput int func (o *fakeErrorOutput) Write([]byte) (int, error) { @@ -876,6 +908,31 @@ func Benchmark_Logger_Parallel(b *testing.B) { benchmarkSetupParallel(bb, app, "/") }) + b.Run("DefaultFormatWithFiberLog", func(bb *testing.B) { + app := fiber.New() + customLoggerFunc := func(c fiber.Ctx, data *Data, cfg Config) error { + cfg.Logger.SetOutput(cfg.Output) + cfg.Logger.SetFlags(0) + cfg.Logger.Infof("%s | %3d | %13v | %15s | %-7s | %-"+data.ErrPaddingStr+"s %s\n", + data.Timestamp.Load().(string), + c.Response().StatusCode(), + data.Stop.Sub(data.Start), + c.IP(), c.Method(), c.Path(), + "", + ) + return nil + } + app.Use(New(Config{ + Output: io.Discard, + Logger: fiberlog.DefaultLogger(), + LoggerFunc: customLoggerFunc, + })) + app.Get("/", func(c fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + benchmarkSetupParallel(bb, app, "/") + }) + b.Run("DefaultFormatDisableColors", func(bb *testing.B) { app := fiber.New() app.Use(New(Config{ From e609bbd8886925985b503a87cc8f4fae1ca8b436 Mon Sep 17 00:00:00 2001 From: Hao Chun Chang Date: Sat, 5 Oct 2024 10:44:19 +0800 Subject: [PATCH 03/21] =?UTF-8?q?=F0=9F=93=9A=20Doc:=20add=20LoggerFunc=20?= =?UTF-8?q?and=20Logger=20to=20middleware=20logger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/middleware/logger.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/middleware/logger.md b/docs/middleware/logger.md index 360aaacf07..596b908c05 100644 --- a/docs/middleware/logger.md +++ b/docs/middleware/logger.md @@ -108,6 +108,8 @@ Writing to os.File is goroutine-safe, but if you are using a custom Output that | TimeZone | `string` | TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc | `"Local"` | | TimeInterval | `time.Duration` | TimeInterval is the delay before the timestamp is updated. | `500 * time.Millisecond` | | Output | `io.Writer` | Output is a writer where logs are written. | `os.Stdout` | +| LoggerFunc | `func(c fiber.Ctx, data *Data, cfg Config) error` | You can use custom loggers with Fiber by using this field. This field is really useful if you're using Zerolog, Zap, Logrus, apex/log etc. If you don't define anything for this field, it'll use default logger of Fiber. | `see default_logger.go defaultLoggerInstance` | +| Logger | `fiberlog.AllLogger` | | | | DisableColors | `bool` | DisableColors defines if the logs output should be colorized. | `false` | | enableColors | `bool` | Internal field for enabling colors in the log output. (This is not a user-configurable field) | - | | enableLatency | `bool` | Internal field for enabling latency measurement in logs. (This is not a user-configurable field) | - | From fc80e6eec8c7b3235646fc0eb6f3e5f943673c0f Mon Sep 17 00:00:00 2001 From: Hao Chun Chang Date: Sun, 6 Oct 2024 09:11:44 +0800 Subject: [PATCH 04/21] :rotating_light: Test: fine-tune custom Logger and LoggerFunc --- middleware/logger/logger_test.go | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 5215da9dd1..b0a9b4e315 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -190,7 +190,7 @@ func Test_Logger_Fiber_Logger(t *testing.T) { buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) - customLoggerFunc := func(c fiber.Ctx, data *Data, cfg Config) error { + customLoggerFunc := func(_ fiber.Ctx, data *Data, cfg Config) error { cfg.Logger.SetOutput(cfg.Output) cfg.Logger.SetFlags(0) cfg.Logger.Error(data.ChainErr.Error()) @@ -765,6 +765,28 @@ func Benchmark_Logger(b *testing.B) { benchmarkSetup(bb, app, "/") }) + b.Run("DefaultFormatWithFiberLog", func(bb *testing.B) { + app := fiber.New() + customLoggerFunc := func(c fiber.Ctx, data *Data, cfg Config) error { + cfg.Logger.SetOutput(cfg.Output) + cfg.Logger.Infof("%3d | %13v | %15s | %-7s | %-"+data.ErrPaddingStr+"s %s\n", + c.Response().StatusCode(), + data.Stop.Sub(data.Start), + c.IP(), c.Method(), c.Path(), "", + ) + return nil + } + app.Use(New(Config{ + Output: io.Discard, + Logger: fiberlog.DefaultLogger(), + LoggerFunc: customLoggerFunc, + })) + app.Get("/", func(c fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + benchmarkSetup(bb, app, "/") + }) + b.Run("WithTagParameter", func(bb *testing.B) { app := fiber.New() app.Use(New(Config{ @@ -912,13 +934,10 @@ func Benchmark_Logger_Parallel(b *testing.B) { app := fiber.New() customLoggerFunc := func(c fiber.Ctx, data *Data, cfg Config) error { cfg.Logger.SetOutput(cfg.Output) - cfg.Logger.SetFlags(0) - cfg.Logger.Infof("%s | %3d | %13v | %15s | %-7s | %-"+data.ErrPaddingStr+"s %s\n", - data.Timestamp.Load().(string), + cfg.Logger.Infof("%3d | %13v | %15s | %-7s | %-"+data.ErrPaddingStr+"s %s\n", c.Response().StatusCode(), data.Stop.Sub(data.Start), - c.IP(), c.Method(), c.Path(), - "", + c.IP(), c.Method(), c.Path(), "", ) return nil } From 3afdcac33eeae1e413a81ca16bcb8a5a520975bc Mon Sep 17 00:00:00 2001 From: Hao Chun Chang Date: Sun, 6 Oct 2024 09:23:36 +0800 Subject: [PATCH 05/21] =?UTF-8?q?=F0=9F=93=9A=20Doc:=20add=20Logger=20docu?= =?UTF-8?q?mentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 📚 Doc: add custom Logger example --- docs/middleware/logger.md | 17 +++++++++++++++-- middleware/logger/config.go | 5 +++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/docs/middleware/logger.md b/docs/middleware/logger.md index 596b908c05..65c2c0f931 100644 --- a/docs/middleware/logger.md +++ b/docs/middleware/logger.md @@ -88,6 +88,19 @@ app.Use(logger.New(logger.Config{ app.Use(logger.New(logger.Config{ DisableColors: true, })) + +// Use Custom Logger with Fiber Logger Interface +customLoggerFunc := func(c fiber.Ctx, data *logger.Data, cfg logger.Config) error { + cfg.Logger.SetOutput(cfg.Output) + cfg.Logger.SetFlags(0) + cfg.Logger.Infof("%3d | %15s | %-7s\n", c.Response().StatusCode(), c.IP(), c.Method()) + return nil +} +app.Use(logger.New(logger.Config{ + Output: buf, + Logger: fiberlog.DefaultLogger(), + LoggerFunc: customLoggerFunc, +})) ``` :::tip @@ -108,8 +121,8 @@ Writing to os.File is goroutine-safe, but if you are using a custom Output that | TimeZone | `string` | TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc | `"Local"` | | TimeInterval | `time.Duration` | TimeInterval is the delay before the timestamp is updated. | `500 * time.Millisecond` | | Output | `io.Writer` | Output is a writer where logs are written. | `os.Stdout` | -| LoggerFunc | `func(c fiber.Ctx, data *Data, cfg Config) error` | You can use custom loggers with Fiber by using this field. This field is really useful if you're using Zerolog, Zap, Logrus, apex/log etc. If you don't define anything for this field, it'll use default logger of Fiber. | `see default_logger.go defaultLoggerInstance` | -| Logger | `fiberlog.AllLogger` | | | +| LoggerFunc | `func(c fiber.Ctx, data *Data, cfg Config) error` | You can use custom loggers with Fiber by using this field. This field is really useful if you're using Zerolog, Zap, Logrus, apex/log etc. If you don't define anything for this field, it'll use the default logger of Fiber. | `see default_logger.go defaultLoggerInstance` | +| Logger | `fiberlog.AllLogger` | Logger allows the use of a custom logger that implements the AllLogger interface. This field can be used in the LoggerFunc to do the logging. If you don't define LoggerFunc, this field will be ignored. | `nil` | | DisableColors | `bool` | DisableColors defines if the logs output should be colorized. | `false` | | enableColors | `bool` | Internal field for enabling colors in the log output. (This is not a user-configurable field) | - | | enableLatency | `bool` | Internal field for enabling latency measurement in logs. (This is not a user-configurable field) | - | diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 1b3799048c..d66eb4704f 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -44,6 +44,11 @@ type Config struct { // Optional. Default: defaultLogger LoggerFunc func(c fiber.Ctx, data *Data, cfg Config) error + // Logger allows the use of a custom logger that implements the AllLogger interface. + // This field can be used in the LoggerFunc to do the logging. + // If you don't define LoggerFunc, it'll use default logger of Fiber without using this field. + // + // Optional. Default: nil Logger fiberlog.AllLogger timeZoneLocation *time.Location From 4fe7adf72378a7aa71ae66e0d736084ea55ea741 Mon Sep 17 00:00:00 2001 From: Hao Chun Chang Date: Thu, 10 Oct 2024 09:00:01 +0800 Subject: [PATCH 06/21] :adhesive_bandage: fix: add default Logger field to default config --- middleware/logger/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/middleware/logger/config.go b/middleware/logger/config.go index d66eb4704f..d6dc282a0b 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -114,6 +114,7 @@ var ConfigDefault = Config{ Output: os.Stdout, BeforeHandlerFunc: beforeHandlerFunc, LoggerFunc: defaultLoggerInstance, + Logger: nil, enableColors: true, } From ae18274b88a45a432c2aeeba5f8e207b9f3d09b6 Mon Sep 17 00:00:00 2001 From: Hao Chun Chang Date: Mon, 14 Oct 2024 22:32:14 +0800 Subject: [PATCH 07/21] =?UTF-8?q?=F0=9F=93=9A=20Doc:=20remove=20Logger=20f?= =?UTF-8?q?ield=20in=20middleware=20logger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 📚 Doc: add example of using fiber logger interface --- docs/middleware/logger.md | 33 +++++++++++++++++++++++---------- middleware/logger/config.go | 9 --------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/docs/middleware/logger.md b/docs/middleware/logger.md index 65c2c0f931..be0bf5da2b 100644 --- a/docs/middleware/logger.md +++ b/docs/middleware/logger.md @@ -90,16 +90,30 @@ app.Use(logger.New(logger.Config{ })) // Use Custom Logger with Fiber Logger Interface -customLoggerFunc := func(c fiber.Ctx, data *logger.Data, cfg logger.Config) error { - cfg.Logger.SetOutput(cfg.Output) - cfg.Logger.SetFlags(0) - cfg.Logger.Infof("%3d | %15s | %-7s\n", c.Response().StatusCode(), c.IP(), c.Method()) - return nil +type dumbLogger struct { + logger fiberlog.AllLogger + level log.Level } + +func (l *dumbLogger) Write(p []byte) (n int, err error) { + switch l.level { + case log.LevelDebug: + l.logger.Debug(string(p)) + case log.LevelError: + l.logger.Error(string(p)) + } + return len(p), nil +} + +func LoggerToWriter(customLogger fiberlog.AllLogger, level fiberlog.Level) io.Writer { + return &dumbLogger{ + logger: customLogger, + level: level, + } +} + app.Use(logger.New(logger.Config{ - Output: buf, - Logger: fiberlog.DefaultLogger(), - LoggerFunc: customLoggerFunc, + Output: LoggerToWriter(fiberlog.DefaultLogger(), fiberlog.LevelError), })) ``` @@ -121,8 +135,7 @@ Writing to os.File is goroutine-safe, but if you are using a custom Output that | TimeZone | `string` | TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc | `"Local"` | | TimeInterval | `time.Duration` | TimeInterval is the delay before the timestamp is updated. | `500 * time.Millisecond` | | Output | `io.Writer` | Output is a writer where logs are written. | `os.Stdout` | -| LoggerFunc | `func(c fiber.Ctx, data *Data, cfg Config) error` | You can use custom loggers with Fiber by using this field. This field is really useful if you're using Zerolog, Zap, Logrus, apex/log etc. If you don't define anything for this field, it'll use the default logger of Fiber. | `see default_logger.go defaultLoggerInstance` | -| Logger | `fiberlog.AllLogger` | Logger allows the use of a custom logger that implements the AllLogger interface. This field can be used in the LoggerFunc to do the logging. If you don't define LoggerFunc, this field will be ignored. | `nil` | +| LoggerFunc | `func(c fiber.Ctx, data *Data, cfg Config) error` | You can use custom loggers with Fiber by using this field. This field is really useful if you're using Zerolog, Zap, Logrus, apex/log etc. If you don't define anything for this field, it'll use the default logger of Fiber. | `see default_logger.go defaultLoggerInstance` | | | DisableColors | `bool` | DisableColors defines if the logs output should be colorized. | `false` | | enableColors | `bool` | Internal field for enabling colors in the log output. (This is not a user-configurable field) | - | | enableLatency | `bool` | Internal field for enabling latency measurement in logs. (This is not a user-configurable field) | - | diff --git a/middleware/logger/config.go b/middleware/logger/config.go index d6dc282a0b..4826151e46 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -6,7 +6,6 @@ import ( "time" "github.com/gofiber/fiber/v3" - fiberlog "github.com/gofiber/fiber/v3/log" ) // Config defines the config for middleware. @@ -44,13 +43,6 @@ type Config struct { // Optional. Default: defaultLogger LoggerFunc func(c fiber.Ctx, data *Data, cfg Config) error - // Logger allows the use of a custom logger that implements the AllLogger interface. - // This field can be used in the LoggerFunc to do the logging. - // If you don't define LoggerFunc, it'll use default logger of Fiber without using this field. - // - // Optional. Default: nil - Logger fiberlog.AllLogger - timeZoneLocation *time.Location // Format defines the logging tags @@ -114,7 +106,6 @@ var ConfigDefault = Config{ Output: os.Stdout, BeforeHandlerFunc: beforeHandlerFunc, LoggerFunc: defaultLoggerInstance, - Logger: nil, enableColors: true, } From ddb55f6e8ed4cdaa1f537da8b078323f7f8695f1 Mon Sep 17 00:00:00 2001 From: Hao Chun Chang Date: Mon, 14 Oct 2024 22:54:42 +0800 Subject: [PATCH 08/21] :rotating_light: Test: add tests for using fiber logger interface wrapper --- middleware/logger/logger_test.go | 58 +++++++++++++------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index b0a9b4e315..9566891278 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -182,6 +182,19 @@ func Test_Logger_ErrorTimeZone(t *testing.T) { require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } +type dumbLogger struct { + logger fiberlog.AllLogger +} + +func (l *dumbLogger) Write(p []byte) (n int, err error) { + l.logger.Error(string(p)) + return len(p), nil +} + +func LoggerToWriter(customLogger fiberlog.AllLogger) io.Writer { + return &dumbLogger{logger: customLogger} +} + // go test -run Test_Logger_Fiber_Logger func Test_Logger_Fiber_Logger(t *testing.T) { t.Parallel() @@ -190,17 +203,12 @@ func Test_Logger_Fiber_Logger(t *testing.T) { buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) - customLoggerFunc := func(_ fiber.Ctx, data *Data, cfg Config) error { - cfg.Logger.SetOutput(cfg.Output) - cfg.Logger.SetFlags(0) - cfg.Logger.Error(data.ChainErr.Error()) - return nil - } - + logger := fiberlog.DefaultLogger() + logger.SetFlags(0) + logger.SetOutput(buf) app.Use(New(Config{ - Output: buf, - Logger: fiberlog.DefaultLogger(), - LoggerFunc: customLoggerFunc, + Format: "${error}", + Output: LoggerToWriter(logger), })) app.Get("/", func(_ fiber.Ctx) error { @@ -767,19 +775,10 @@ func Benchmark_Logger(b *testing.B) { b.Run("DefaultFormatWithFiberLog", func(bb *testing.B) { app := fiber.New() - customLoggerFunc := func(c fiber.Ctx, data *Data, cfg Config) error { - cfg.Logger.SetOutput(cfg.Output) - cfg.Logger.Infof("%3d | %13v | %15s | %-7s | %-"+data.ErrPaddingStr+"s %s\n", - c.Response().StatusCode(), - data.Stop.Sub(data.Start), - c.IP(), c.Method(), c.Path(), "", - ) - return nil - } + logger := fiberlog.DefaultLogger() + logger.SetOutput(io.Discard) app.Use(New(Config{ - Output: io.Discard, - Logger: fiberlog.DefaultLogger(), - LoggerFunc: customLoggerFunc, + Output: LoggerToWriter(logger), })) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") @@ -932,19 +931,10 @@ func Benchmark_Logger_Parallel(b *testing.B) { b.Run("DefaultFormatWithFiberLog", func(bb *testing.B) { app := fiber.New() - customLoggerFunc := func(c fiber.Ctx, data *Data, cfg Config) error { - cfg.Logger.SetOutput(cfg.Output) - cfg.Logger.Infof("%3d | %13v | %15s | %-7s | %-"+data.ErrPaddingStr+"s %s\n", - c.Response().StatusCode(), - data.Stop.Sub(data.Start), - c.IP(), c.Method(), c.Path(), "", - ) - return nil - } + logger := fiberlog.DefaultLogger() + logger.SetOutput(io.Discard) app.Use(New(Config{ - Output: io.Discard, - Logger: fiberlog.DefaultLogger(), - LoggerFunc: customLoggerFunc, + Output: LoggerToWriter(logger), })) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") From eb093d5facb7d4990ada28e84c2c52bdcb17ce52 Mon Sep 17 00:00:00 2001 From: Hao Chun Chang Date: Sun, 24 Nov 2024 19:58:31 +0800 Subject: [PATCH 09/21] =?UTF-8?q?=F0=9F=93=9A=20Doc:=20update=20custom=20l?= =?UTF-8?q?ogger=20example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/middleware/logger.md | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/docs/middleware/logger.md b/docs/middleware/logger.md index be0bf5da2b..1e41e392b4 100644 --- a/docs/middleware/logger.md +++ b/docs/middleware/logger.md @@ -90,26 +90,20 @@ app.Use(logger.New(logger.Config{ })) // Use Custom Logger with Fiber Logger Interface -type dumbLogger struct { - logger fiberlog.AllLogger - level log.Level -} - -func (l *dumbLogger) Write(p []byte) (n int, err error) { - switch l.level { - case log.LevelDebug: - l.logger.Debug(string(p)) - case log.LevelError: - l.logger.Error(string(p)) - } - return len(p), nil -} - func LoggerToWriter(customLogger fiberlog.AllLogger, level fiberlog.Level) io.Writer { - return &dumbLogger{ - logger: customLogger, - level: level, - } + return &struct { + Write func(p []byte) (n int, err error) + }{ + Write: func(p []byte) (n int, err error) { + switch level { + case fiberlog.LevelDebug: + customLogger.Debug(string(p)) + case fiberlog.LevelError: + customLogger.Error(string(p)) + } + return len(p), nil + }, + } } app.Use(logger.New(logger.Config{ From a88e612a3482538615a100c268ec89085f9abdb4 Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 29 Nov 2024 09:58:00 +0100 Subject: [PATCH 10/21] Update docs/middleware/logger.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- docs/middleware/logger.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/middleware/logger.md b/docs/middleware/logger.md index 1e41e392b4..e8262671ac 100644 --- a/docs/middleware/logger.md +++ b/docs/middleware/logger.md @@ -129,7 +129,7 @@ Writing to os.File is goroutine-safe, but if you are using a custom Output that | TimeZone | `string` | TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc | `"Local"` | | TimeInterval | `time.Duration` | TimeInterval is the delay before the timestamp is updated. | `500 * time.Millisecond` | | Output | `io.Writer` | Output is a writer where logs are written. | `os.Stdout` | -| LoggerFunc | `func(c fiber.Ctx, data *Data, cfg Config) error` | You can use custom loggers with Fiber by using this field. This field is really useful if you're using Zerolog, Zap, Logrus, apex/log etc. If you don't define anything for this field, it'll use the default logger of Fiber. | `see default_logger.go defaultLoggerInstance` | | +| LoggerFunc | `func(c fiber.Ctx, data *Data, cfg Config) error` | Custom logger function for integration with logging libraries (Zerolog, Zap, Logrus, etc). Defaults to Fiber's default logger if not defined. | `see default_logger.go defaultLoggerInstance` | | DisableColors | `bool` | DisableColors defines if the logs output should be colorized. | `false` | | enableColors | `bool` | Internal field for enabling colors in the log output. (This is not a user-configurable field) | - | | enableLatency | `bool` | Internal field for enabling latency measurement in logs. (This is not a user-configurable field) | - | From 8454f0c93b2342c44a9e05458e9fb6a516ae38c3 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Fri, 29 Nov 2024 19:41:03 +0300 Subject: [PATCH 11/21] update --- docs/middleware/logger.md | 54 +++++++++++++++++++++----------- middleware/logger/logger_test.go | 22 ++++++++----- middleware/logger/utils.go | 47 +++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 26 deletions(-) diff --git a/docs/middleware/logger.md b/docs/middleware/logger.md index e8262671ac..8166b432f8 100644 --- a/docs/middleware/logger.md +++ b/docs/middleware/logger.md @@ -88,27 +88,43 @@ app.Use(logger.New(logger.Config{ app.Use(logger.New(logger.Config{ DisableColors: true, })) +``` -// Use Custom Logger with Fiber Logger Interface -func LoggerToWriter(customLogger fiberlog.AllLogger, level fiberlog.Level) io.Writer { - return &struct { - Write func(p []byte) (n int, err error) - }{ - Write: func(p []byte) (n int, err error) { - switch level { - case fiberlog.LevelDebug: - customLogger.Debug(string(p)) - case fiberlog.LevelError: - customLogger.Error(string(p)) - } - return len(p), nil - }, - } -} +### Use Logger Middleware with Other Loggers -app.Use(logger.New(logger.Config{ - Output: LoggerToWriter(fiberlog.DefaultLogger(), fiberlog.LevelError), -})) +In order to use Fiber logger middleware with other loggers such as zerolog, zap, logrus; you can use `LoggerToWriter` helper which converts Fiber logger to a writer, which is compatible with the middleware. + +```go +package main + +import ( + "github.com/gofiber/contrib/fiberzap/v2" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/log" + "github.com/gofiber/fiber/v3/middleware/logger" +) + +func main() { + // Create a new Fiber instance + app := fiber.New() + + zap := fiberzap.NewLogger(fiberzap.LoggerConfig{ + ExtraKeys: []string{"request_id"}, + }) + + // Use the logger middleware with zerolog logger + app.Use(logger.New(logger.Config{ + Output: logger.LoggerToWriter(zap, log.LevelDebug), + })) + + // Define a route + app.Get("/", func(c fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + // Start server on http://localhost:3000 + app.Listen(":3000") +} ``` :::tip diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 9566891278..9cde4c7dd4 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -191,10 +191,6 @@ func (l *dumbLogger) Write(p []byte) (n int, err error) { return len(p), nil } -func LoggerToWriter(customLogger fiberlog.AllLogger) io.Writer { - return &dumbLogger{logger: customLogger} -} - // go test -run Test_Logger_Fiber_Logger func Test_Logger_Fiber_Logger(t *testing.T) { t.Parallel() @@ -208,7 +204,7 @@ func Test_Logger_Fiber_Logger(t *testing.T) { logger.SetOutput(buf) app.Use(New(Config{ Format: "${error}", - Output: LoggerToWriter(logger), + Output: LoggerToWriter(logger, fiberlog.LevelError), })) app.Get("/", func(_ fiber.Ctx) error { @@ -219,6 +215,18 @@ func Test_Logger_Fiber_Logger(t *testing.T) { require.NoError(t, err) require.Equal(t, fiber.StatusInternalServerError, resp.StatusCode) require.Equal(t, "[Error] some random error\n", buf.String()) + + require.Panics(t, func() { + LoggerToWriter(logger, fiberlog.LevelPanic) + }) + + require.Panics(t, func() { + LoggerToWriter(logger, fiberlog.LevelFatal) + }) + + require.Panics(t, func() { + LoggerToWriter(nil, fiberlog.LevelFatal) + }) } type fakeErrorOutput int @@ -778,7 +786,7 @@ func Benchmark_Logger(b *testing.B) { logger := fiberlog.DefaultLogger() logger.SetOutput(io.Discard) app.Use(New(Config{ - Output: LoggerToWriter(logger), + Output: LoggerToWriter(logger, fiberlog.LevelDebug), })) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") @@ -934,7 +942,7 @@ func Benchmark_Logger_Parallel(b *testing.B) { logger := fiberlog.DefaultLogger() logger.SetOutput(io.Discard) app.Use(New(Config{ - Output: LoggerToWriter(logger), + Output: LoggerToWriter(logger, fiberlog.LevelDebug), })) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") diff --git a/middleware/logger/utils.go b/middleware/logger/utils.go index 5c0432718c..43acf2e35b 100644 --- a/middleware/logger/utils.go +++ b/middleware/logger/utils.go @@ -1,7 +1,11 @@ package logger import ( + "io" + "github.com/gofiber/fiber/v3" + fiberlog "github.com/gofiber/fiber/v3/log" + "github.com/gofiber/utils/v2" ) func methodColor(method string, colors fiber.Colors) string { @@ -37,3 +41,46 @@ func statusColor(code int, colors fiber.Colors) string { return colors.Red } } + +type customLoggerWriter struct { + level fiberlog.Level + loggerInstance fiberlog.AllLogger +} + +func (cl *customLoggerWriter) Write(p []byte) (n int, err error) { + switch cl.level { + case fiberlog.LevelInfo: + cl.loggerInstance.Info(utils.UnsafeString(p)) + case fiberlog.LevelTrace: + cl.loggerInstance.Trace(utils.UnsafeString(p)) + case fiberlog.LevelWarn: + cl.loggerInstance.Warn(utils.UnsafeString(p)) + case fiberlog.LevelDebug: + cl.loggerInstance.Debug(utils.UnsafeString(p)) + case fiberlog.LevelError: + cl.loggerInstance.Error(utils.UnsafeString(p)) + } + + return len(p), nil +} + +// LoggerToWriter is a helper function that returns an io.Writer that writes to a custom logger. +// You can integrate 3rd party loggers such as zerolog, logrus, etc. to logger middleware using this function. +// +// Valid levels: fiberlog.LevelInfo, fiberlog.LevelTrace, fiberlog.LevelWarn, fiberlog.LevelDebug, fiberlog.LevelError +func LoggerToWriter(customLogger fiberlog.AllLogger, level fiberlog.Level) io.Writer { + // Check if customLogger is nil + if customLogger == nil { + fiberlog.Panic("LoggerToWriter: customLogger must not be nil") + } + + // Check if level is valid + if level == fiberlog.LevelFatal || level == fiberlog.LevelPanic { + fiberlog.Panic("LoggerToWriter: invalid level") + } + + return &customLoggerWriter{ + level: level, + loggerInstance: customLogger, + } +} From 86c837d62b08d3cd295ac6b7cb424a60c8e326f7 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Fri, 29 Nov 2024 20:09:20 +0300 Subject: [PATCH 12/21] update logger docs --- docs/middleware/logger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/middleware/logger.md b/docs/middleware/logger.md index 8166b432f8..69885e02ac 100644 --- a/docs/middleware/logger.md +++ b/docs/middleware/logger.md @@ -163,6 +163,7 @@ var ConfigDefault = Config{ TimeInterval: 500 * time.Millisecond, Output: os.Stdout, DisableColors: false, + LoggerFunc: defaultLoggerInstance, } ``` From ed708f506f53b01e1a6e02c7ee104f5129bd7624 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Fri, 29 Nov 2024 20:15:59 +0300 Subject: [PATCH 13/21] update what's new --- docs/middleware/logger.md | 1 + docs/whats_new.md | 43 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/docs/middleware/logger.md b/docs/middleware/logger.md index 69885e02ac..43be5e9f90 100644 --- a/docs/middleware/logger.md +++ b/docs/middleware/logger.md @@ -108,6 +108,7 @@ func main() { // Create a new Fiber instance app := fiber.New() + // Create a new zap logger which is compatible with Fiber AllLogger interface zap := fiberzap.NewLogger(fiberzap.LoggerConfig{ ExtraKeys: []string{"request_id"}, }) diff --git a/docs/whats_new.md b/docs/whats_new.md index 274a7799c8..329b7cf30a 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -26,6 +26,7 @@ Here's a quick overview of the changes in Fiber `v3`: - [CORS](#cors) - [CSRF](#csrf) - [Session](#session) + - [Logger](#logger) - [Filesystem](#filesystem) - [Monitor](#monitor) - [Healthcheck](#healthcheck) @@ -704,6 +705,48 @@ The Session middleware has undergone key changes in v3 to improve functionality For more details on these changes and migration instructions, check the [Session Middleware Migration Guide](./middleware/session.md#migration-guide). +### Logger + +New helper function called `LoggerToWriter` has been added to the logger middleware. +This function allows you to use 3rd party loggers such as `logrus` or `zap` with the Fiber logger middleware without any extra afford. For example, you can use `zap` with Fiber logger middleware like this: + +```go +package main + +import ( + "github.com/gofiber/contrib/fiberzap/v2" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/log" + "github.com/gofiber/fiber/v3/middleware/logger" +) + +func main() { + // Create a new Fiber instance + app := fiber.New() + + // Create a new zap logger which is compatible with Fiber AllLogger interface + zap := fiberzap.NewLogger(fiberzap.LoggerConfig{ + ExtraKeys: []string{"request_id"}, + }) + + // Use the logger middleware with zerolog logger + app.Use(logger.New(logger.Config{ + Output: logger.LoggerToWriter(zap, log.LevelDebug), + })) + + // Define a route + app.Get("/", func(c fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + // Start server on http://localhost:3000 + app.Listen(":3000") +} +``` + + + + ### Filesystem We've decided to remove filesystem middleware to clear up the confusion between static and filesystem middleware. From 1a09cb616d659f13dd04c38049df8582bdb4eccb Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Fri, 29 Nov 2024 20:39:56 +0300 Subject: [PATCH 14/21] replace setflags with getloggerinstance --- docs/api/log.md | 16 ++++++++++++++++ docs/whats_new.md | 7 +++++++ log/default.go | 5 +++++ log/log.go | 11 ++++++++++- middleware/logger/logger_test.go | 7 ++++++- 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/docs/api/log.md b/docs/api/log.md index 53f964a7dc..84af8367f0 100644 --- a/docs/api/log.md +++ b/docs/api/log.md @@ -186,3 +186,19 @@ commonLogger.Info("info") ``` Binding the logger to a context allows you to include context-specific information in your logs, improving traceability and debugging. + +## GetLoggerInstance +You can use GetLoggerInstance to retrieve the logger instance. It is useful when you need to access underlying methods of the logger. +To retrieve the logger instance, use the following method: + +```go +logger := fiberlog.DefaultLogger() // Call DefaultLogger to get the default logger instance + +stdlogger, ok := logger.GetLoggerInstance().(*log.Logger) // Get the logger instance and assert it to *log.Logger +if !ok { + panic("logger is not *log.Logger") +} + +stdlogger.SetFlags(0) // Hide timestamp by setting flags to 0 +``` + diff --git a/docs/whats_new.md b/docs/whats_new.md index 329b7cf30a..5930662be7 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -22,6 +22,7 @@ Here's a quick overview of the changes in Fiber `v3`: - [🔄️ Redirect](#-redirect) - [🌎 Client package](#-client-package) - [🧰 Generic functions](#-generic-functions) +- [📃 Log](#-log) - [🧬 Middlewares](#-middlewares) - [CORS](#cors) - [CSRF](#csrf) @@ -630,6 +631,12 @@ curl "http://localhost:3000/header" +## 📃 Log + +`fiber.AllLogger` interface now has a new method called `GetLoggerInstance`. This method can be used to get the underlying logger instance from the Fiber logger middleware. This is useful when you want to configure the logger middleware with a custom logger and still want to access the underlying logger instance. + +You can find more details about this feature in [/docs/api/log.md](./api/log.md#getloggerinstance). + ## 🧬 Middlewares ### Adaptor diff --git a/log/default.go b/log/default.go index 86287059fa..23219ca51b 100644 --- a/log/default.go +++ b/log/default.go @@ -210,6 +210,11 @@ func (l *defaultLogger) SetOutput(writer io.Writer) { l.stdlog.SetOutput(writer) } +// GetLoggerInstance returns the logger instance. It can be used to adjust the logger configurations in case of need. +func (l *defaultLogger) GetLoggerInstance() any { + return l.stdlog +} + func (l *defaultLogger) SetFlags(flag int) { l.stdlog.SetFlags(flag) } diff --git a/log/log.go b/log/log.go index 5fac67e035..5bea2e5565 100644 --- a/log/log.go +++ b/log/log.go @@ -54,9 +54,16 @@ type CommonLogger interface { // ControlLogger provides methods to config a logger. type ControlLogger interface { + // SetLevel sets logging level. + // + // Available levels: Trace, Debug, Info, Warn, Error, Fatal, Panic. SetLevel(level Level) + + // SetOutput sets the logger output. SetOutput(w io.Writer) - SetFlags(flags int) + + // GetLoggerInstance returns the logger instance. It can be used to adjust the logger configurations in case of need. + GetLoggerInstance() any } // AllLogger is the combination of Logger, FormatLogger, CtxLogger and ControlLogger. @@ -64,6 +71,8 @@ type ControlLogger interface { type AllLogger interface { CommonLogger ControlLogger + + // WithContext returns a new logger with the given context. WithContext(ctx context.Context) CommonLogger } diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 9cde4c7dd4..f96b6db25f 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "log" "net/http" "net/http/httptest" "os" @@ -200,8 +201,12 @@ func Test_Logger_Fiber_Logger(t *testing.T) { defer bytebufferpool.Put(buf) logger := fiberlog.DefaultLogger() - logger.SetFlags(0) + stdlogger, ok := logger.GetLoggerInstance().(*log.Logger) + require.True(t, ok) + + stdlogger.SetFlags(0) logger.SetOutput(buf) + app.Use(New(Config{ Format: "${error}", Output: LoggerToWriter(logger, fiberlog.LevelError), From ef57ea34ec94069e67ece9af3f3547f9f3786400 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Fri, 29 Nov 2024 20:43:56 +0300 Subject: [PATCH 15/21] fix linter --- middleware/logger/logger_test.go | 10 +--------- middleware/logger/utils.go | 6 ++++-- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index f96b6db25f..49f06160c7 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -1,3 +1,4 @@ +//nolint:depguard // Because we test logging :D package logger import ( @@ -183,15 +184,6 @@ func Test_Logger_ErrorTimeZone(t *testing.T) { require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } -type dumbLogger struct { - logger fiberlog.AllLogger -} - -func (l *dumbLogger) Write(p []byte) (n int, err error) { - l.logger.Error(string(p)) - return len(p), nil -} - // go test -run Test_Logger_Fiber_Logger func Test_Logger_Fiber_Logger(t *testing.T) { t.Parallel() diff --git a/middleware/logger/utils.go b/middleware/logger/utils.go index 43acf2e35b..62e6bee057 100644 --- a/middleware/logger/utils.go +++ b/middleware/logger/utils.go @@ -43,11 +43,11 @@ func statusColor(code int, colors fiber.Colors) string { } type customLoggerWriter struct { - level fiberlog.Level loggerInstance fiberlog.AllLogger + level fiberlog.Level } -func (cl *customLoggerWriter) Write(p []byte) (n int, err error) { +func (cl *customLoggerWriter) Write(p []byte) (int, error) { switch cl.level { case fiberlog.LevelInfo: cl.loggerInstance.Info(utils.UnsafeString(p)) @@ -59,6 +59,8 @@ func (cl *customLoggerWriter) Write(p []byte) (n int, err error) { cl.loggerInstance.Debug(utils.UnsafeString(p)) case fiberlog.LevelError: cl.loggerInstance.Error(utils.UnsafeString(p)) + default: + return 0, nil } return len(p), nil From 2b2b5fe6f93334c3ee53f7bf786dbb75a8c135c5 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Fri, 29 Nov 2024 20:45:11 +0300 Subject: [PATCH 16/21] update --- docs/api/log.md | 1 + docs/whats_new.md | 3 --- log/default.go | 4 ---- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/docs/api/log.md b/docs/api/log.md index 84af8367f0..b63f78d08c 100644 --- a/docs/api/log.md +++ b/docs/api/log.md @@ -188,6 +188,7 @@ commonLogger.Info("info") Binding the logger to a context allows you to include context-specific information in your logs, improving traceability and debugging. ## GetLoggerInstance + You can use GetLoggerInstance to retrieve the logger instance. It is useful when you need to access underlying methods of the logger. To retrieve the logger instance, use the following method: diff --git a/docs/whats_new.md b/docs/whats_new.md index 5930662be7..974988106d 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -751,9 +751,6 @@ func main() { } ``` - - - ### Filesystem We've decided to remove filesystem middleware to clear up the confusion between static and filesystem middleware. diff --git a/log/default.go b/log/default.go index 23219ca51b..fd6c1f6312 100644 --- a/log/default.go +++ b/log/default.go @@ -215,10 +215,6 @@ func (l *defaultLogger) GetLoggerInstance() any { return l.stdlog } -func (l *defaultLogger) SetFlags(flag int) { - l.stdlog.SetFlags(flag) -} - // DefaultLogger returns the default logger. func DefaultLogger() AllLogger { return logger From 25f2952165cd123386fa51325d0cd184768b8f5a Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Sat, 30 Nov 2024 09:44:29 -0500 Subject: [PATCH 17/21] Fix markdownlint issues --- docs/api/log.md | 1 - docs/middleware/logger.md | 44 ++++++++++++++++---------------- docs/whats_new.md | 53 +++++++++++++++++++-------------------- 3 files changed, 48 insertions(+), 50 deletions(-) diff --git a/docs/api/log.md b/docs/api/log.md index b63f78d08c..cff4689f6e 100644 --- a/docs/api/log.md +++ b/docs/api/log.md @@ -202,4 +202,3 @@ if !ok { stdlogger.SetFlags(0) // Hide timestamp by setting flags to 0 ``` - diff --git a/docs/middleware/logger.md b/docs/middleware/logger.md index 43be5e9f90..07ff07c4de 100644 --- a/docs/middleware/logger.md +++ b/docs/middleware/logger.md @@ -98,33 +98,33 @@ In order to use Fiber logger middleware with other loggers such as zerolog, zap, package main import ( - "github.com/gofiber/contrib/fiberzap/v2" - "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/log" - "github.com/gofiber/fiber/v3/middleware/logger" + "github.com/gofiber/contrib/fiberzap/v2" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/log" + "github.com/gofiber/fiber/v3/middleware/logger" ) func main() { - // Create a new Fiber instance - app := fiber.New() + // Create a new Fiber instance + app := fiber.New() // Create a new zap logger which is compatible with Fiber AllLogger interface - zap := fiberzap.NewLogger(fiberzap.LoggerConfig{ - ExtraKeys: []string{"request_id"}, - }) - - // Use the logger middleware with zerolog logger - app.Use(logger.New(logger.Config{ - Output: logger.LoggerToWriter(zap, log.LevelDebug), - })) - - // Define a route - app.Get("/", func(c fiber.Ctx) error { - return c.SendString("Hello, World!") - }) - - // Start server on http://localhost:3000 - app.Listen(":3000") + zap := fiberzap.NewLogger(fiberzap.LoggerConfig{ + ExtraKeys: []string{"request_id"}, + }) + + // Use the logger middleware with zerolog logger + app.Use(logger.New(logger.Config{ + Output: logger.LoggerToWriter(zap, log.LevelDebug), + })) + + // Define a route + app.Get("/", func(c fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + // Start server on http://localhost:3000 + app.Listen(":3000") } ``` diff --git a/docs/whats_new.md b/docs/whats_new.md index 974988106d..0b7d6f9fa0 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -633,7 +633,7 @@ curl "http://localhost:3000/header" ## 📃 Log -`fiber.AllLogger` interface now has a new method called `GetLoggerInstance`. This method can be used to get the underlying logger instance from the Fiber logger middleware. This is useful when you want to configure the logger middleware with a custom logger and still want to access the underlying logger instance. +`fiber.AllLogger` interface now has a new method called `GetLoggerInstance`. This method can be used to get the underlying logger instance from the Fiber logger middleware. This is useful when you want to configure the logger middleware with a custom logger and still want to access the underlying logger instance. You can find more details about this feature in [/docs/api/log.md](./api/log.md#getloggerinstance). @@ -714,40 +714,39 @@ For more details on these changes and migration instructions, check the [Session ### Logger -New helper function called `LoggerToWriter` has been added to the logger middleware. -This function allows you to use 3rd party loggers such as `logrus` or `zap` with the Fiber logger middleware without any extra afford. For example, you can use `zap` with Fiber logger middleware like this: +New helper function called `LoggerToWriter` has been added to the logger middleware. This function allows you to use 3rd party loggers such as `logrus` or `zap` with the Fiber logger middleware without any extra afford. For example, you can use `zap` with Fiber logger middleware like this: ```go package main import ( - "github.com/gofiber/contrib/fiberzap/v2" - "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/log" - "github.com/gofiber/fiber/v3/middleware/logger" + "github.com/gofiber/contrib/fiberzap/v2" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/log" + "github.com/gofiber/fiber/v3/middleware/logger" ) func main() { - // Create a new Fiber instance - app := fiber.New() - - // Create a new zap logger which is compatible with Fiber AllLogger interface - zap := fiberzap.NewLogger(fiberzap.LoggerConfig{ - ExtraKeys: []string{"request_id"}, - }) - - // Use the logger middleware with zerolog logger - app.Use(logger.New(logger.Config{ - Output: logger.LoggerToWriter(zap, log.LevelDebug), - })) - - // Define a route - app.Get("/", func(c fiber.Ctx) error { - return c.SendString("Hello, World!") - }) - - // Start server on http://localhost:3000 - app.Listen(":3000") + // Create a new Fiber instance + app := fiber.New() + + // Create a new zap logger which is compatible with Fiber AllLogger interface + zap := fiberzap.NewLogger(fiberzap.LoggerConfig{ + ExtraKeys: []string{"request_id"}, + }) + + // Use the logger middleware with zerolog logger + app.Use(logger.New(logger.Config{ + Output: logger.LoggerToWriter(zap, log.LevelDebug), + })) + + // Define a route + app.Get("/", func(c fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + // Start server on http://localhost:3000 + app.Listen(":3000") } ``` From 6bbb4b9313f01815020ebdfefd39677971e8dd87 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Sun, 1 Dec 2024 14:38:44 +0300 Subject: [PATCH 18/21] apply reviews & improve coverage --- docs/api/log.md | 8 ++-- docs/whats_new.md | 4 +- log/default.go | 4 +- log/default_test.go | 16 +++++++ log/log.go | 4 +- middleware/logger/logger_test.go | 79 +++++++++++++++++++++++--------- middleware/logger/utils.go | 14 +++--- 7 files changed, 90 insertions(+), 39 deletions(-) diff --git a/docs/api/log.md b/docs/api/log.md index b63f78d08c..cd77b3d4e0 100644 --- a/docs/api/log.md +++ b/docs/api/log.md @@ -187,15 +187,15 @@ commonLogger.Info("info") Binding the logger to a context allows you to include context-specific information in your logs, improving traceability and debugging. -## GetLoggerInstance +## Logger -You can use GetLoggerInstance to retrieve the logger instance. It is useful when you need to access underlying methods of the logger. -To retrieve the logger instance, use the following method: +You can use Logger to retrieve the logger instance. It is useful when you need to access underlying methods of the logger. +To retrieve the Logger instance, use the following method: ```go logger := fiberlog.DefaultLogger() // Call DefaultLogger to get the default logger instance -stdlogger, ok := logger.GetLoggerInstance().(*log.Logger) // Get the logger instance and assert it to *log.Logger +stdlogger, ok := logger.Logger().(*log.Logger) // Get the logger instance and assert it to *log.Logger if !ok { panic("logger is not *log.Logger") } diff --git a/docs/whats_new.md b/docs/whats_new.md index 974988106d..30029e9c40 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -633,9 +633,9 @@ curl "http://localhost:3000/header" ## 📃 Log -`fiber.AllLogger` interface now has a new method called `GetLoggerInstance`. This method can be used to get the underlying logger instance from the Fiber logger middleware. This is useful when you want to configure the logger middleware with a custom logger and still want to access the underlying logger instance. +`fiber.AllLogger` interface now has a new method called `Logger`. This method can be used to get the underlying logger instance from the Fiber logger middleware. This is useful when you want to configure the logger middleware with a custom logger and still want to access the underlying logger instance. -You can find more details about this feature in [/docs/api/log.md](./api/log.md#getloggerinstance). +You can find more details about this feature in [/docs/api/log.md](./api/log.md#logger). ## 🧬 Middlewares diff --git a/log/default.go b/log/default.go index fd6c1f6312..6de940a418 100644 --- a/log/default.go +++ b/log/default.go @@ -210,8 +210,8 @@ func (l *defaultLogger) SetOutput(writer io.Writer) { l.stdlog.SetOutput(writer) } -// GetLoggerInstance returns the logger instance. It can be used to adjust the logger configurations in case of need. -func (l *defaultLogger) GetLoggerInstance() any { +// Logger returns the logger instance. It can be used to adjust the logger configurations in case of need. +func (l *defaultLogger) Logger() any { return l.stdlog } diff --git a/log/default_test.go b/log/default_test.go index 4a6ff82e59..86a276bc81 100644 --- a/log/default_test.go +++ b/log/default_test.go @@ -221,6 +221,22 @@ func Test_SetLevel(t *testing.T) { require.Equal(t, "[?8] ", setLogger.level.toString()) } +func Test_Logger(t *testing.T) { + underlyingLogger := log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds) + setLogger := &defaultLogger{ + stdlog: underlyingLogger, + depth: 4, + } + + require.Equal(t, underlyingLogger, setLogger.Logger()) + + logger, ok := setLogger.Logger().(*log.Logger) + require.True(t, ok) + + logger.SetFlags(log.LstdFlags | log.Lshortfile | log.Lmicroseconds) + require.Equal(t, log.LstdFlags|log.Lshortfile|log.Lmicroseconds, setLogger.stdlog.Flags()) +} + func Test_Debugw(t *testing.T) { initDefaultLogger() diff --git a/log/log.go b/log/log.go index 5bea2e5565..8affdb67a9 100644 --- a/log/log.go +++ b/log/log.go @@ -62,8 +62,8 @@ type ControlLogger interface { // SetOutput sets the logger output. SetOutput(w io.Writer) - // GetLoggerInstance returns the logger instance. It can be used to adjust the logger configurations in case of need. - GetLoggerInstance() any + // Logger returns the logger instance. It can be used to adjust the logger configurations in case of need. + Logger() any } // AllLogger is the combination of Logger, FormatLogger, CtxLogger and ControlLogger. diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 49f06160c7..d0cbe4cd3e 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -12,6 +12,7 @@ import ( "net/http/httptest" "os" "runtime" + "strconv" "sync" "testing" "time" @@ -185,7 +186,7 @@ func Test_Logger_ErrorTimeZone(t *testing.T) { } // go test -run Test_Logger_Fiber_Logger -func Test_Logger_Fiber_Logger(t *testing.T) { +func Test_Logger_LoggerToWriter(t *testing.T) { t.Parallel() app := fiber.New() @@ -193,37 +194,71 @@ func Test_Logger_Fiber_Logger(t *testing.T) { defer bytebufferpool.Put(buf) logger := fiberlog.DefaultLogger() - stdlogger, ok := logger.GetLoggerInstance().(*log.Logger) + stdlogger, ok := logger.Logger().(*log.Logger) require.True(t, ok) stdlogger.SetFlags(0) logger.SetOutput(buf) - app.Use(New(Config{ - Format: "${error}", - Output: LoggerToWriter(logger, fiberlog.LevelError), - })) + testCases := []struct { + level fiberlog.Level + levelStr string + }{ + { + level: fiberlog.LevelTrace, + levelStr: "Trace", + }, + { + level: fiberlog.LevelDebug, + levelStr: "Debug", + }, + { + level: fiberlog.LevelInfo, + levelStr: "Info", + }, + { + level: fiberlog.LevelWarn, + levelStr: "Warn", + }, + { + level: fiberlog.LevelError, + levelStr: "Error", + }, + } - app.Get("/", func(_ fiber.Ctx) error { - return errors.New("some random error") - }) + for _, tc := range testCases { + level := strconv.Itoa(int(tc.level)) + t.Run(level, func(t *testing.T) { + buf.Reset() - resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) - require.NoError(t, err) - require.Equal(t, fiber.StatusInternalServerError, resp.StatusCode) - require.Equal(t, "[Error] some random error\n", buf.String()) + app.Use("/"+level, New(Config{ + Format: "${error}", + Output: LoggerToWriter(logger, tc. + level), + })) - require.Panics(t, func() { - LoggerToWriter(logger, fiberlog.LevelPanic) - }) + app.Get("/"+level, func(_ fiber.Ctx) error { + return errors.New("some random error") + }) - require.Panics(t, func() { - LoggerToWriter(logger, fiberlog.LevelFatal) - }) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/"+level, nil)) + require.NoError(t, err) + require.Equal(t, fiber.StatusInternalServerError, resp.StatusCode) + require.Equal(t, "["+tc.levelStr+"] some random error\n", buf.String()) + }) - require.Panics(t, func() { - LoggerToWriter(nil, fiberlog.LevelFatal) - }) + require.Panics(t, func() { + LoggerToWriter(logger, fiberlog.LevelPanic) + }) + + require.Panics(t, func() { + LoggerToWriter(logger, fiberlog.LevelFatal) + }) + + require.Panics(t, func() { + LoggerToWriter(nil, fiberlog.LevelFatal) + }) + } } type fakeErrorOutput int diff --git a/middleware/logger/utils.go b/middleware/logger/utils.go index 62e6bee057..bab734794a 100644 --- a/middleware/logger/utils.go +++ b/middleware/logger/utils.go @@ -49,14 +49,14 @@ type customLoggerWriter struct { func (cl *customLoggerWriter) Write(p []byte) (int, error) { switch cl.level { - case fiberlog.LevelInfo: - cl.loggerInstance.Info(utils.UnsafeString(p)) case fiberlog.LevelTrace: cl.loggerInstance.Trace(utils.UnsafeString(p)) - case fiberlog.LevelWarn: - cl.loggerInstance.Warn(utils.UnsafeString(p)) case fiberlog.LevelDebug: cl.loggerInstance.Debug(utils.UnsafeString(p)) + case fiberlog.LevelInfo: + cl.loggerInstance.Info(utils.UnsafeString(p)) + case fiberlog.LevelWarn: + cl.loggerInstance.Warn(utils.UnsafeString(p)) case fiberlog.LevelError: cl.loggerInstance.Error(utils.UnsafeString(p)) default: @@ -70,9 +70,9 @@ func (cl *customLoggerWriter) Write(p []byte) (int, error) { // You can integrate 3rd party loggers such as zerolog, logrus, etc. to logger middleware using this function. // // Valid levels: fiberlog.LevelInfo, fiberlog.LevelTrace, fiberlog.LevelWarn, fiberlog.LevelDebug, fiberlog.LevelError -func LoggerToWriter(customLogger fiberlog.AllLogger, level fiberlog.Level) io.Writer { +func LoggerToWriter(logger fiberlog.AllLogger, level fiberlog.Level) io.Writer { // Check if customLogger is nil - if customLogger == nil { + if logger == nil { fiberlog.Panic("LoggerToWriter: customLogger must not be nil") } @@ -83,6 +83,6 @@ func LoggerToWriter(customLogger fiberlog.AllLogger, level fiberlog.Level) io.Wr return &customLoggerWriter{ level: level, - loggerInstance: customLogger, + loggerInstance: logger, } } From c3a5a1e160a4720b08a649aa082094e927790386 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Sun, 1 Dec 2024 14:43:51 +0300 Subject: [PATCH 19/21] fix linter --- middleware/logger/logger_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index d0cbe4cd3e..d459f22c59 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -187,11 +187,12 @@ func Test_Logger_ErrorTimeZone(t *testing.T) { // go test -run Test_Logger_Fiber_Logger func Test_Logger_LoggerToWriter(t *testing.T) { - t.Parallel() app := fiber.New() buf := bytebufferpool.Get() - defer bytebufferpool.Put(buf) + t.Cleanup(func() { + bytebufferpool.Put(buf) + }) logger := fiberlog.DefaultLogger() stdlogger, ok := logger.Logger().(*log.Logger) @@ -201,8 +202,8 @@ func Test_Logger_LoggerToWriter(t *testing.T) { logger.SetOutput(buf) testCases := []struct { - level fiberlog.Level levelStr string + level fiberlog.Level }{ { level: fiberlog.LevelTrace, From d89b1cce40733a3ffc3ff9292ca04974ddb52268 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Sun, 1 Dec 2024 14:48:16 +0300 Subject: [PATCH 20/21] rename controllogger --- docs/api/log.md | 2 +- log/log.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/api/log.md b/docs/api/log.md index fae98a9718..91af92276b 100644 --- a/docs/api/log.md +++ b/docs/api/log.md @@ -36,7 +36,7 @@ type CommonLogger interface { type AllLogger interface { CommonLogger - ControlLogger + ConfigurableLogger WithLogger } ``` diff --git a/log/log.go b/log/log.go index 8affdb67a9..4a7fd5fe0b 100644 --- a/log/log.go +++ b/log/log.go @@ -52,8 +52,8 @@ type CommonLogger interface { WithLogger } -// ControlLogger provides methods to config a logger. -type ControlLogger interface { +// ConfigurableLogger provides methods to config a logger. +type ConfigurableLogger interface { // SetLevel sets logging level. // // Available levels: Trace, Debug, Info, Warn, Error, Fatal, Panic. @@ -66,11 +66,11 @@ type ControlLogger interface { Logger() any } -// AllLogger is the combination of Logger, FormatLogger, CtxLogger and ControlLogger. +// AllLogger is the combination of Logger, FormatLogger, CtxLogger and ConfigurableLogger. // Custom extensions can be made through AllLogger type AllLogger interface { CommonLogger - ControlLogger + ConfigurableLogger // WithContext returns a new logger with the given context. WithContext(ctx context.Context) CommonLogger From 3c27970789143c672fe074fcb90dbd5edd705c43 Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 1 Dec 2024 13:13:08 +0100 Subject: [PATCH 21/21] Update whats_new.md expandable example --- docs/whats_new.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/whats_new.md b/docs/whats_new.md index bca0ad0880..957e484bf1 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -717,6 +717,9 @@ For more details on these changes and migration instructions, check the [Session New helper function called `LoggerToWriter` has been added to the logger middleware. This function allows you to use 3rd party loggers such as `logrus` or `zap` with the Fiber logger middleware without any extra afford. For example, you can use `zap` with Fiber logger middleware like this: +
+Example + ```go package main @@ -751,6 +754,8 @@ func main() { } ``` +
+ ### Filesystem We've decided to remove filesystem middleware to clear up the confusion between static and filesystem middleware.