Skip to content

Commit 408fc2d

Browse files
committed
chore: refactor everything so slog.Logger can satisfy LoggerWithLevel interface
1 parent a0b35c6 commit 408fc2d

File tree

17 files changed

+327
-139
lines changed

17 files changed

+327
-139
lines changed

async_handoff_integration_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import (
77
"testing"
88
"time"
99

10-
"github.com/redis/go-redis/v9/maintnotifications"
1110
"github.com/redis/go-redis/v9/internal/pool"
1211
"github.com/redis/go-redis/v9/logging"
12+
"github.com/redis/go-redis/v9/maintnotifications"
1313
)
1414

1515
// mockNetConn implements net.Conn for testing

internal/log.go

Lines changed: 0 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -77,66 +77,3 @@ func (l LogLevelT) InfoOrAbove() bool {
7777
func (l LogLevelT) DebugOrAbove() bool {
7878
return l >= LogLevelDebug
7979
}
80-
81-
// LoggerWithLevel is a logger interface with leveled logging methods.
82-
//
83-
// This interface can be implemented by custom loggers to provide leveled logging.
84-
type LoggerWithLevel interface {
85-
// Infof logs an info level message
86-
Infof(ctx context.Context, format string, v ...interface{})
87-
88-
// Warnf logs a warning level message
89-
Warnf(ctx context.Context, format string, v ...interface{})
90-
91-
// Debugf logs a debug level message
92-
Debugf(ctx context.Context, format string, v ...interface{})
93-
94-
// Errorf logs an error level message
95-
Errorf(ctx context.Context, format string, v ...interface{})
96-
97-
// Enabled reports whether the given log level is enabled in the logger
98-
Enabled(ctx context.Context, level LogLevelT) bool
99-
}
100-
101-
// legacyLoggerAdapter is a logger that implements LoggerWithLevel interface
102-
// using the global [Logger] and [LogLevel] variables.
103-
type legacyLoggerAdapter struct{}
104-
105-
func (l *legacyLoggerAdapter) Infof(ctx context.Context, format string, v ...interface{}) {
106-
if LogLevel.InfoOrAbove() {
107-
Logger.Printf(ctx, format, v...)
108-
}
109-
}
110-
111-
func (l *legacyLoggerAdapter) Warnf(ctx context.Context, format string, v ...interface{}) {
112-
if LogLevel.WarnOrAbove() {
113-
Logger.Printf(ctx, format, v...)
114-
}
115-
}
116-
117-
func (l *legacyLoggerAdapter) Debugf(ctx context.Context, format string, v ...interface{}) {
118-
if LogLevel.DebugOrAbove() {
119-
Logger.Printf(ctx, format, v...)
120-
}
121-
}
122-
123-
func (l legacyLoggerAdapter) Errorf(ctx context.Context, format string, v ...interface{}) {
124-
Logger.Printf(ctx, format, v...)
125-
}
126-
127-
func (l legacyLoggerAdapter) Enabled(_ context.Context, level LogLevelT) bool {
128-
switch level {
129-
case LogLevelWarn:
130-
return LogLevel.WarnOrAbove()
131-
case LogLevelInfo:
132-
return LogLevel.InfoOrAbove()
133-
case LogLevelDebug:
134-
return LogLevel.DebugOrAbove()
135-
case LogLevelError:
136-
fallthrough
137-
default:
138-
return true
139-
}
140-
}
141-
142-
var LegacyLoggerWithLevel LoggerWithLevel = &legacyLoggerAdapter{}

internal/pool/pool.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/redis/go-redis/v9/internal"
1212
"github.com/redis/go-redis/v9/internal/proto"
1313
"github.com/redis/go-redis/v9/internal/util"
14+
"github.com/redis/go-redis/v9/logging"
1415
)
1516

1617
var (
@@ -117,7 +118,7 @@ type Options struct {
117118
DialerRetryTimeout time.Duration
118119

119120
// Optional logger for connection pool operations.
120-
Logger internal.LoggerWithLevel
121+
Logger *logging.CustomLogger
121122
}
122123

123124
type lastDialErrorWrap struct {
@@ -965,10 +966,10 @@ func (p *ConnPool) isHealthyConn(cn *Conn, now time.Time) bool {
965966
return true
966967
}
967968

968-
func (p *ConnPool) logger() internal.LoggerWithLevel {
969-
if p.cfg.Logger != nil {
970-
return p.cfg.Logger
969+
func (p *ConnPool) logger() *logging.CustomLogger {
970+
var logger *logging.CustomLogger
971+
if p.cfg != nil && p.cfg.Logger != nil {
972+
logger = p.cfg.Logger
971973
}
972-
973-
return internal.LegacyLoggerWithLevel
974+
return logger
974975
}

logging/custom.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package logging
2+
3+
import (
4+
"context"
5+
"fmt"
6+
)
7+
8+
// CustomLogger is a logger interface with leveled logging methods.
9+
//
10+
// This interface can be implemented by custom loggers to provide leveled logging.
11+
type CustomLogger struct {
12+
logger LoggerWithLevel
13+
loggerLevel *LogLevelT
14+
printfAdapter PrintfAdapter
15+
}
16+
17+
func NewCustomLogger(logger LoggerWithLevel, opts ...CustomLoggerOption) *CustomLogger {
18+
cl := &CustomLogger{
19+
logger: logger,
20+
}
21+
for _, opt := range opts {
22+
opt(cl)
23+
}
24+
return cl
25+
}
26+
27+
type CustomLoggerOption func(*CustomLogger)
28+
29+
func WithPrintfAdapter(adapter PrintfAdapter) CustomLoggerOption {
30+
return func(cl *CustomLogger) {
31+
cl.printfAdapter = adapter
32+
}
33+
}
34+
35+
func WithLoggerLevel(level LogLevelT) CustomLoggerOption {
36+
return func(cl *CustomLogger) {
37+
cl.loggerLevel = &level
38+
}
39+
}
40+
41+
// PrintfAdapter is a function that converts Printf-style log messages into structured log messages.
42+
// It can be used to extract key-value pairs from the formatted message.
43+
type PrintfAdapter func(ctx context.Context, format string, v ...any) (context.Context, string, []any)
44+
45+
// Error is a structured error level logging method with context and arguments.
46+
func (cl *CustomLogger) Error(ctx context.Context, msg string, args ...any) {
47+
if cl == nil || cl.logger == nil {
48+
legacyLoggerWithLevel.Errorf(ctx, msg, args...)
49+
return
50+
}
51+
cl.logger.ErrorContext(ctx, msg, args...)
52+
}
53+
54+
func (cl *CustomLogger) Errorf(ctx context.Context, format string, v ...any) {
55+
if cl == nil || cl.logger == nil {
56+
legacyLoggerWithLevel.Errorf(ctx, format, v...)
57+
return
58+
}
59+
cl.logger.ErrorContext(ctx, format, v...)
60+
}
61+
62+
// Warn is a structured warning level logging method with context and arguments.
63+
func (cl *CustomLogger) Warn(ctx context.Context, msg string, args ...any) {
64+
if cl == nil || cl.logger == nil {
65+
legacyLoggerWithLevel.Warnf(ctx, msg, args...)
66+
return
67+
}
68+
cl.logger.WarnContext(ctx, msg, args...)
69+
}
70+
71+
func (cl *CustomLogger) Warnf(ctx context.Context, format string, v ...any) {
72+
if cl == nil || cl.logger == nil {
73+
legacyLoggerWithLevel.Warnf(ctx, format, v...)
74+
return
75+
}
76+
cl.logger.WarnContext(cl.printfToStructured(ctx, format, v...))
77+
}
78+
79+
// Info is a structured info level logging method with context and arguments.
80+
func (cl *CustomLogger) Info(ctx context.Context, msg string, args ...any) {
81+
if cl == nil || cl.logger == nil {
82+
legacyLoggerWithLevel.Infof(ctx, msg, args...)
83+
return
84+
}
85+
cl.logger.InfoContext(ctx, msg, args...)
86+
}
87+
88+
// Debug is a structured debug level logging method with context and arguments.
89+
func (cl *CustomLogger) Debug(ctx context.Context, msg string, args ...any) {
90+
if cl == nil || cl.logger == nil {
91+
legacyLoggerWithLevel.Debugf(ctx, msg, args...)
92+
return
93+
}
94+
cl.logger.DebugContext(ctx, msg, args...)
95+
}
96+
97+
func (cl *CustomLogger) Infof(ctx context.Context, format string, v ...any) {
98+
if cl == nil || cl.logger == nil {
99+
legacyLoggerWithLevel.Infof(ctx, format, v...)
100+
return
101+
}
102+
103+
cl.logger.InfoContext(cl.printfToStructured(ctx, format, v...))
104+
}
105+
106+
func (cl *CustomLogger) Debugf(ctx context.Context, format string, v ...any) {
107+
if cl == nil || cl.logger == nil {
108+
legacyLoggerWithLevel.Debugf(ctx, format, v...)
109+
return
110+
}
111+
cl.logger.DebugContext(cl.printfToStructured(ctx, format, v...))
112+
}
113+
114+
func (cl *CustomLogger) printfToStructured(ctx context.Context, format string, v ...any) (context.Context, string, []any) {
115+
if cl.printfAdapter != nil {
116+
return cl.printfAdapter(ctx, format, v...)
117+
}
118+
return ctx, fmt.Sprintf(format, v...), nil
119+
}
120+
121+
func (cl *CustomLogger) Enabled(ctx context.Context, level LogLevelT) bool {
122+
if cl.loggerLevel != nil {
123+
return level >= *cl.loggerLevel
124+
}
125+
126+
return legacyLoggerWithLevel.Enabled(ctx, level)
127+
}
128+
129+
// LoggerWithLevel is a logger interface with leveled logging methods.
130+
//
131+
// [slog.Logger] from the standard library satisfies this interface.
132+
type LoggerWithLevel interface {
133+
// InfoContext logs an info level message
134+
InfoContext(ctx context.Context, format string, v ...any)
135+
136+
// WarnContext logs a warning level message
137+
WarnContext(ctx context.Context, format string, v ...any)
138+
139+
// Debugf logs a debug level message
140+
DebugContext(ctx context.Context, format string, v ...any)
141+
142+
// Errorf logs an error level message
143+
ErrorContext(ctx context.Context, format string, v ...any)
144+
}

logging/legacy.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package logging
2+
3+
import (
4+
"context"
5+
6+
"github.com/redis/go-redis/v9/internal"
7+
)
8+
9+
// legacyLoggerAdapter is a logger that implements [LoggerWithLevel] interface
10+
// using the global [internal.Logger] and [internal.LogLevel] variables.
11+
type legacyLoggerAdapter struct{}
12+
13+
var _ LoggerWithLevel = (*legacyLoggerAdapter)(nil)
14+
15+
// structuredToPrintf converts a structured log message and key-value pairs into something a Printf-style logger can understand.
16+
func (l *legacyLoggerAdapter) structuredToPrintf(msg string, v ...any) (string, []any) {
17+
format := msg
18+
var args []any
19+
20+
for i := 0; i < len(v); i += 2 {
21+
if i+1 >= len(v) {
22+
break
23+
}
24+
format += " %v=%v"
25+
args = append(args, v[i], v[i+1])
26+
}
27+
28+
return format, args
29+
}
30+
31+
func (l legacyLoggerAdapter) Errorf(ctx context.Context, format string, v ...any) {
32+
internal.Logger.Printf(ctx, format, v...)
33+
}
34+
35+
func (l *legacyLoggerAdapter) ErrorContext(ctx context.Context, msg string, args ...any) {
36+
format, v := l.structuredToPrintf(msg, args...)
37+
l.Errorf(ctx, format, v...)
38+
}
39+
40+
func (l *legacyLoggerAdapter) WarnContext(ctx context.Context, msg string, args ...any) {
41+
format, v := l.structuredToPrintf(msg, args...)
42+
l.Warnf(ctx, format, v...)
43+
}
44+
45+
func (l *legacyLoggerAdapter) Warnf(ctx context.Context, format string, v ...any) {
46+
if !internal.LogLevel.WarnOrAbove() {
47+
// Skip logging
48+
return
49+
}
50+
internal.Logger.Printf(ctx, format, v...)
51+
}
52+
53+
func (l *legacyLoggerAdapter) InfoContext(ctx context.Context, msg string, args ...any) {
54+
format, v := l.structuredToPrintf(msg, args...)
55+
l.Infof(ctx, format, v...)
56+
}
57+
58+
func (l *legacyLoggerAdapter) Infof(ctx context.Context, format string, v ...any) {
59+
if !internal.LogLevel.InfoOrAbove() {
60+
// Skip logging
61+
return
62+
}
63+
internal.Logger.Printf(ctx, format, v...)
64+
}
65+
66+
func (l *legacyLoggerAdapter) DebugContext(ctx context.Context, msg string, args ...any) {
67+
format, v := l.structuredToPrintf(msg, args...)
68+
l.Debugf(ctx, format, v...)
69+
}
70+
71+
func (l *legacyLoggerAdapter) Debugf(ctx context.Context, format string, v ...any) {
72+
if !internal.LogLevel.DebugOrAbove() {
73+
// Skip logging
74+
return
75+
}
76+
internal.Logger.Printf(ctx, format, v...)
77+
}
78+
79+
func (l *legacyLoggerAdapter) Enabled(ctx context.Context, level LogLevelT) bool {
80+
switch level {
81+
case LogLevelDebug:
82+
return internal.LogLevel.DebugOrAbove()
83+
case LogLevelWarn:
84+
return internal.LogLevel.WarnOrAbove()
85+
case LogLevelInfo:
86+
return internal.LogLevel.InfoOrAbove()
87+
}
88+
return true
89+
}
90+
91+
var legacyLoggerWithLevel = &legacyLoggerAdapter{}

maintnotifications/circuit_breaker.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import (
66
"sync/atomic"
77
"time"
88

9-
"github.com/redis/go-redis/v9/internal"
109
"github.com/redis/go-redis/v9/internal/maintnotifications/logs"
10+
"github.com/redis/go-redis/v9/logging"
1111
)
1212

1313
// CircuitBreakerState represents the state of a circuit breaker
@@ -194,11 +194,12 @@ func (cb *CircuitBreaker) GetStats() CircuitBreakerStats {
194194
}
195195
}
196196

197-
func (cb *CircuitBreaker) logger() internal.LoggerWithLevel {
197+
func (cb *CircuitBreaker) logger() *logging.CustomLogger {
198+
var logger *logging.CustomLogger
198199
if cb.config != nil && cb.config.Logger != nil {
199-
return cb.config.Logger
200+
logger = cb.config.Logger
200201
}
201-
return internal.LegacyLoggerWithLevel
202+
return logger
202203
}
203204

204205
// CircuitBreakerStats provides statistics about a circuit breaker
@@ -351,9 +352,10 @@ func (cbm *CircuitBreakerManager) Reset() {
351352
})
352353
}
353354

354-
func (cbm *CircuitBreakerManager) logger() internal.LoggerWithLevel {
355+
func (cbm *CircuitBreakerManager) logger() *logging.CustomLogger {
356+
var logger *logging.CustomLogger
355357
if cbm.config != nil && cbm.config.Logger != nil {
356-
return cbm.config.Logger
358+
logger = cbm.config.Logger
357359
}
358-
return internal.LegacyLoggerWithLevel
360+
return logger
359361
}

0 commit comments

Comments
 (0)