Skip to content

Commit 64a7113

Browse files
feat(log): support generic configurable logger (#3705)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent d0ca515 commit 64a7113

File tree

10 files changed

+68
-43
lines changed

10 files changed

+68
-43
lines changed

client/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,7 @@ func NewWithClient(c *fasthttp.Client) *Client {
662662
cborMarshal: cbor.Marshal,
663663
cborUnmarshal: cbor.Unmarshal,
664664
xmlUnmarshal: xml.Unmarshal,
665-
logger: log.DefaultLogger(),
665+
logger: log.DefaultLogger[*log.Logger](),
666666
}
667667
}
668668

docs/api/log.md

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const (
2525

2626
## Custom Log
2727

28-
Fiber provides the `AllLogger` interface for adapting various log libraries.
28+
Fiber provides the generic `AllLogger[T]` interface for adapting various log libraries.
2929

3030
```go
3131
type CommonLogger interface {
@@ -34,9 +34,20 @@ type CommonLogger interface {
3434
WithLogger
3535
}
3636

37-
type AllLogger interface {
37+
type ConfigurableLogger[T any] interface {
38+
// SetLevel sets logging level.
39+
SetLevel(level Level)
40+
41+
// SetOutput sets the logger output.
42+
SetOutput(w io.Writer)
43+
44+
// Logger returns the logger instance.
45+
Logger() T
46+
}
47+
48+
type AllLogger[T any] interface {
3849
CommonLogger
39-
ConfigurableLogger
50+
ConfigurableLogger[T]
4051
WithLogger
4152
}
4253
```
@@ -106,7 +117,7 @@ import (
106117
fiberlog "github.com/gofiber/fiber/v3/log"
107118
)
108119

109-
var _ fiberlog.AllLogger = (*customLogger)(nil)
120+
var _ fiberlog.AllLogger[*log.Logger] = (*customLogger)(nil)
110121

111122
type customLogger struct {
112123
stdlog *log.Logger
@@ -115,9 +126,13 @@ type customLogger struct {
115126
// Implement required methods for the AllLogger interface...
116127

117128
// Inject your custom logger
118-
fiberlog.SetLogger(&customLogger{
129+
fiberlog.SetLogger[*log.Logger](&customLogger{
119130
stdlog: log.New(os.Stdout, "CUSTOM ", log.LstdFlags),
120131
})
132+
133+
// Retrieve the underlying *log.Logger for direct use
134+
std := fiberlog.DefaultLogger[*log.Logger]().Logger()
135+
std.Println("custom logging")
121136
```
122137

123138
## Set Level
@@ -141,7 +156,7 @@ Setting the log level allows you to control the verbosity of the logs, filtering
141156
### Writing Logs to Stderr
142157

143158
```go
144-
var logger fiberlog.AllLogger = &defaultLogger{
159+
var logger fiberlog.AllLogger[*log.Logger] = &defaultLogger{
145160
stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds),
146161
depth: 4,
147162
}
@@ -193,12 +208,8 @@ You can use Logger to retrieve the logger instance. It is useful when you need t
193208
To retrieve the Logger instance, use the following method:
194209

195210
```go
196-
logger := fiberlog.DefaultLogger() // Call DefaultLogger to get the default logger instance
197-
198-
stdlogger, ok := logger.Logger().(*log.Logger) // Get the logger instance and assert it to *log.Logger
199-
if !ok {
200-
panic("logger is not *log.Logger")
201-
}
211+
logger := fiberlog.DefaultLogger[*log.Logger]() // Get the default logger instance
202212

213+
stdlogger := logger.Logger() // stdlogger is *log.Logger
203214
stdlogger.SetFlags(0) // Hide timestamp by setting flags to 0
204215
```

docs/whats_new.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,7 @@ INFO Total process count: 1
995995

996996
## 📃 Log
997997

998-
`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.
998+
`fiber.AllLogger[T]` interface now has a new generic type parameter `T` and a 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 with the appropriate type.
999999

10001000
You can find more details about this feature in [/docs/api/log.md](./api/log.md#logger).
10011001

@@ -1436,6 +1436,7 @@ fiber migrate --to 3.0.0
14361436
- [🧠 Context](#-context-1)
14371437
- [📎 Binding (was Parser)](#-parser)
14381438
- [🔄 Redirect](#-redirect-1)
1439+
- [🧾 Log](#-log-1)
14391440
- [🌎 Client package](#-client-package-1)
14401441
- [🛠️ Utils](#utils-migration)
14411442
- [🧬 Middlewares](#-middlewares-1)
@@ -1903,6 +1904,10 @@ Fiber v3 enhances the redirect functionality by introducing new methods and impr
19031904

19041905
</details>
19051906

1907+
#### 🧾 Log
1908+
1909+
The `ConfigurableLogger` and `AllLogger` interfaces now use generics. You can specify the underlying logger type when implementing these interfaces. While `any` can be used for maximum flexibility in some contexts, when retrieving the concrete logger via `log.DefaultLogger`, you must specify the exact underlying logger type, for example `log.DefaultLogger[*MyLogger]().Logger()`.
1910+
19061911
### 🌎 Client package
19071912

19081913
Fiber v3 introduces a completely rebuilt client package with numerous new features such as Cookiejar, request/response hooks, and more. Here is a guide to help you migrate from Fiber v2 to Fiber v3.

log/default.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"github.com/valyala/bytebufferpool"
1212
)
1313

14-
var _ AllLogger = (*defaultLogger)(nil)
14+
var _ AllLogger[*log.Logger] = (*defaultLogger)(nil)
1515

1616
type defaultLogger struct {
1717
stdlog *log.Logger
@@ -211,11 +211,15 @@ func (l *defaultLogger) SetOutput(writer io.Writer) {
211211
}
212212

213213
// Logger returns the logger instance. It can be used to adjust the logger configurations in case of need.
214-
func (l *defaultLogger) Logger() any {
214+
func (l *defaultLogger) Logger() *log.Logger {
215215
return l.stdlog
216216
}
217217

218218
// DefaultLogger returns the default logger.
219-
func DefaultLogger() AllLogger {
220-
return logger
219+
func DefaultLogger[T any]() AllLogger[T] {
220+
if l, ok := logger.(AllLogger[T]); ok {
221+
return l
222+
}
223+
224+
return nil
221225
}

log/default_test.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,7 @@ func Test_Logger(t *testing.T) {
230230

231231
require.Equal(t, underlyingLogger, setLogger.Logger())
232232

233-
logger, ok := setLogger.Logger().(*log.Logger)
234-
require.True(t, ok)
235-
233+
logger := setLogger.Logger()
236234
logger.SetFlags(log.LstdFlags | log.Lshortfile | log.Lmicroseconds)
237235
require.Equal(t, log.LstdFlags|log.Lshortfile|log.Lmicroseconds, setLogger.stdlog.Flags())
238236
}

log/fiberlog.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ func WithContext(ctx context.Context) CommonLogger {
123123

124124
// SetLogger sets the default logger and the system logger.
125125
// Note that this method is not concurrent-safe and must not be called
126-
// after the use of DefaultLogger and global functions privateLog this package.
127-
func SetLogger(v AllLogger) {
126+
// after the use of DefaultLogger and global functions from this package.
127+
func SetLogger[T any](v AllLogger[T]) {
128128
logger = v
129129
}
130130

log/fiberlog_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010

1111
func Test_DefaultSystemLogger(t *testing.T) {
1212
t.Parallel()
13-
defaultL := DefaultLogger()
13+
defaultL := DefaultLogger[*log.Logger]()
1414
require.Equal(t, logger, defaultL)
1515
}
1616

@@ -73,7 +73,7 @@ func Test_Fiberlog_SetLevel(t *testing.T) {
7373
func Benchmark_DefaultSystemLogger(b *testing.B) {
7474
b.ReportAllocs()
7575
for b.Loop() {
76-
_ = DefaultLogger()
76+
_ = DefaultLogger[*log.Logger]()
7777
}
7878
}
7979

@@ -141,7 +141,7 @@ func Benchmark_DefaultSystemLogger_Parallel(b *testing.B) {
141141
b.ResetTimer()
142142
b.RunParallel(func(pb *testing.PB) {
143143
for pb.Next() {
144-
_ = DefaultLogger()
144+
_ = DefaultLogger[*log.Logger]()
145145
}
146146
})
147147
}

log/log.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,16 @@ import (
88
"os"
99
)
1010

11-
var logger AllLogger = &defaultLogger{
11+
// baseLogger defines the minimal logger functionality required by the package.
12+
// It allows storing any logger implementation regardless of its generic type.
13+
type baseLogger interface {
14+
CommonLogger
15+
SetLevel(Level)
16+
SetOutput(io.Writer)
17+
WithContext(ctx context.Context) CommonLogger
18+
}
19+
20+
var logger baseLogger = &defaultLogger{
1221
stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds),
1322
depth: 4,
1423
}
@@ -53,7 +62,7 @@ type CommonLogger interface {
5362
}
5463

5564
// ConfigurableLogger provides methods to config a logger.
56-
type ConfigurableLogger interface {
65+
type ConfigurableLogger[T any] interface {
5766
// SetLevel sets logging level.
5867
//
5968
// Available levels: Trace, Debug, Info, Warn, Error, Fatal, Panic.
@@ -63,14 +72,14 @@ type ConfigurableLogger interface {
6372
SetOutput(w io.Writer)
6473

6574
// Logger returns the logger instance. It can be used to adjust the logger configurations in case of need.
66-
Logger() any
75+
Logger() T
6776
}
6877

6978
// AllLogger is the combination of Logger, FormatLogger, CtxLogger and ConfigurableLogger.
7079
// Custom extensions can be made through AllLogger
71-
type AllLogger interface {
80+
type AllLogger[T any] interface {
7281
CommonLogger
73-
ConfigurableLogger
82+
ConfigurableLogger[T]
7483

7584
// WithContext returns a new logger with the given context.
7685
WithContext(ctx context.Context) CommonLogger

middleware/logger/logger_test.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -341,10 +341,8 @@ func Test_Logger_LoggerToWriter(t *testing.T) {
341341
bytebufferpool.Put(buf)
342342
})
343343

344-
logger := fiberlog.DefaultLogger()
345-
stdlogger, ok := logger.Logger().(*log.Logger)
346-
require.True(t, ok)
347-
344+
logger := fiberlog.DefaultLogger[*log.Logger]()
345+
stdlogger := logger.Logger()
348346
stdlogger.SetFlags(0)
349347
logger.SetOutput(buf)
350348

@@ -404,7 +402,7 @@ func Test_Logger_LoggerToWriter(t *testing.T) {
404402
})
405403

406404
require.Panics(t, func() {
407-
LoggerToWriter(nil, fiberlog.LevelFatal)
405+
LoggerToWriter[any](nil, fiberlog.LevelFatal)
408406
})
409407
}
410408
}
@@ -1104,7 +1102,7 @@ func Benchmark_Logger(b *testing.B) {
11041102

11051103
b.Run("DefaultFormatWithFiberLog", func(bb *testing.B) {
11061104
app := fiber.New()
1107-
logger := fiberlog.DefaultLogger()
1105+
logger := fiberlog.DefaultLogger[*log.Logger]()
11081106
logger.SetOutput(io.Discard)
11091107
app.Use(New(Config{
11101108
Stream: LoggerToWriter(logger, fiberlog.LevelDebug),
@@ -1260,7 +1258,7 @@ func Benchmark_Logger_Parallel(b *testing.B) {
12601258

12611259
b.Run("DefaultFormatWithFiberLog", func(bb *testing.B) {
12621260
app := fiber.New()
1263-
logger := fiberlog.DefaultLogger()
1261+
logger := fiberlog.DefaultLogger[*log.Logger]()
12641262
logger.SetOutput(io.Discard)
12651263
app.Use(New(Config{
12661264
Stream: LoggerToWriter(logger, fiberlog.LevelDebug),

middleware/logger/utils.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ func statusColor(code int, colors fiber.Colors) string {
4242
}
4343
}
4444

45-
type customLoggerWriter struct {
46-
loggerInstance fiberlog.AllLogger
45+
type customLoggerWriter[T any] struct {
46+
loggerInstance fiberlog.AllLogger[T]
4747
level fiberlog.Level
4848
}
4949

50-
func (cl *customLoggerWriter) Write(p []byte) (int, error) {
50+
func (cl *customLoggerWriter[T]) Write(p []byte) (int, error) {
5151
switch cl.level {
5252
case fiberlog.LevelTrace:
5353
cl.loggerInstance.Trace(utils.UnsafeString(p))
@@ -70,7 +70,7 @@ func (cl *customLoggerWriter) Write(p []byte) (int, error) {
7070
// You can integrate 3rd party loggers such as zerolog, logrus, etc. to logger middleware using this function.
7171
//
7272
// Valid levels: fiberlog.LevelInfo, fiberlog.LevelTrace, fiberlog.LevelWarn, fiberlog.LevelDebug, fiberlog.LevelError
73-
func LoggerToWriter(logger fiberlog.AllLogger, level fiberlog.Level) io.Writer {
73+
func LoggerToWriter[T any](logger fiberlog.AllLogger[T], level fiberlog.Level) io.Writer {
7474
// Check if customLogger is nil
7575
if logger == nil {
7676
fiberlog.Panic("LoggerToWriter: customLogger must not be nil")
@@ -81,7 +81,7 @@ func LoggerToWriter(logger fiberlog.AllLogger, level fiberlog.Level) io.Writer {
8181
fiberlog.Panic("LoggerToWriter: invalid level")
8282
}
8383

84-
return &customLoggerWriter{
84+
return &customLoggerWriter[T]{
8585
level: level,
8686
loggerInstance: logger,
8787
}

0 commit comments

Comments
 (0)