diff --git a/docs/middleware/logger.md b/docs/middleware/logger.md index 360aaacf07..1e41e392b4 100644 --- a/docs/middleware/logger.md +++ b/docs/middleware/logger.md @@ -88,6 +88,27 @@ 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 + }, + } +} + +app.Use(logger.New(logger.Config{ + Output: LoggerToWriter(fiberlog.DefaultLogger(), fiberlog.LevelError), +})) ``` :::tip @@ -108,6 +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` | | | 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/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/logger_test.go b/middleware/logger/logger_test.go index 946db05d80..9566891278 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,45 @@ 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() + app := fiber.New() + + buf := bytebufferpool.Get() + defer bytebufferpool.Put(buf) + + logger := fiberlog.DefaultLogger() + logger.SetFlags(0) + logger.SetOutput(buf) + app.Use(New(Config{ + Format: "${error}", + Output: LoggerToWriter(logger), + })) + + 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) { @@ -733,6 +773,19 @@ func Benchmark_Logger(b *testing.B) { benchmarkSetup(bb, app, "/") }) + b.Run("DefaultFormatWithFiberLog", func(bb *testing.B) { + app := fiber.New() + logger := fiberlog.DefaultLogger() + logger.SetOutput(io.Discard) + app.Use(New(Config{ + Output: LoggerToWriter(logger), + })) + 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{ @@ -876,6 +929,19 @@ func Benchmark_Logger_Parallel(b *testing.B) { benchmarkSetupParallel(bb, app, "/") }) + b.Run("DefaultFormatWithFiberLog", func(bb *testing.B) { + app := fiber.New() + logger := fiberlog.DefaultLogger() + logger.SetOutput(io.Discard) + app.Use(New(Config{ + Output: LoggerToWriter(logger), + })) + 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{