mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-05-27 12:52:27 +02:00
refactor: migrate from zerolog to zap logging library
Replace github.com/rs/zerolog with go.uber.org/zap throughout the codebase to improve performance and provide better structured logging capabilities. Key changes: - Replace zerolog imports with zap and zapcore - Implement custom Logger wrapper in log.go to maintain zerolog-like API - Add LogEvent struct with chained methods (Str, Int, Err, Bool, etc.) - Update all logging calls to use the new zap-based wrapper - Replace JSON encoders with Console encoders for better readability Benefits: - Better performance with zap's optimized logging - Consistent structured logging across all components - Maintained zerolog-like API for easy migration - Proper field context preservation for debugging - Multi-core logging architecture for better output control All tests pass and build succeeds.
This commit is contained in:
committed by
Cuong Manh Le
parent
016c566307
commit
d41334c66f
@@ -3,8 +3,11 @@ package ctrld
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
// LoggerCtxKey is the context.Context key for a logger.
|
||||
@@ -17,13 +20,13 @@ func LoggerCtx(ctx context.Context, l *Logger) context.Context {
|
||||
|
||||
// A Logger provides fast, leveled, structured logging.
|
||||
type Logger struct {
|
||||
*zerolog.Logger
|
||||
*zap.Logger
|
||||
}
|
||||
|
||||
var noOpZeroLogger = zerolog.Nop()
|
||||
var noOpZapLogger = zap.NewNop()
|
||||
|
||||
// NopLogger returns a logger which all operation are no-op.
|
||||
var NopLogger = &Logger{&noOpZeroLogger}
|
||||
var NopLogger = &Logger{noOpZapLogger}
|
||||
|
||||
// LoggerFromCtx returns the logger associated with given ctx.
|
||||
//
|
||||
@@ -38,9 +41,80 @@ func LoggerFromCtx(ctx context.Context) *Logger {
|
||||
// ReqIdCtxKey is the context.Context key for a request id.
|
||||
type ReqIdCtxKey struct{}
|
||||
|
||||
// Log emits the logs for a particular zerolog event.
|
||||
// LogEvent represents a logging event with structured fields
|
||||
type LogEvent struct {
|
||||
logger *zap.Logger
|
||||
level zapcore.Level
|
||||
fields []zap.Field
|
||||
}
|
||||
|
||||
// Msg logs the message with the collected fields
|
||||
func (e *LogEvent) Msg(msg string) {
|
||||
e.logger.Check(e.level, msg).Write(e.fields...)
|
||||
}
|
||||
|
||||
// Msgf logs a formatted message with the collected fields
|
||||
func (e *LogEvent) Msgf(format string, v ...any) {
|
||||
e.Msg(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// MsgFunc logs a message from a function with the collected fields
|
||||
func (e *LogEvent) MsgFunc(fn func() string) {
|
||||
e.Msg(fn())
|
||||
}
|
||||
|
||||
// Str adds a string field to the event
|
||||
func (e *LogEvent) Str(key, val string) *LogEvent {
|
||||
e.fields = append(e.fields, zap.String(key, val))
|
||||
return e
|
||||
}
|
||||
|
||||
// Int adds an integer field to the event
|
||||
func (e *LogEvent) Int(key string, val int) *LogEvent {
|
||||
e.fields = append(e.fields, zap.Int(key, val))
|
||||
return e
|
||||
}
|
||||
|
||||
// Int64 adds an int64 field to the event
|
||||
func (e *LogEvent) Int64(key string, val int64) *LogEvent {
|
||||
e.fields = append(e.fields, zap.Int64(key, val))
|
||||
return e
|
||||
}
|
||||
|
||||
// Err adds an error field to the event
|
||||
func (e *LogEvent) Err(err error) *LogEvent {
|
||||
if err != nil {
|
||||
e.fields = append(e.fields, zap.Error(err))
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// Bool adds a boolean field to the event
|
||||
func (e *LogEvent) Bool(key string, val bool) *LogEvent {
|
||||
e.fields = append(e.fields, zap.Bool(key, val))
|
||||
return e
|
||||
}
|
||||
|
||||
// Interface adds an interface field to the event
|
||||
func (e *LogEvent) Interface(key string, val interface{}) *LogEvent {
|
||||
e.fields = append(e.fields, zap.Any(key, val))
|
||||
return e
|
||||
}
|
||||
|
||||
// Any adds an interface field to the event (alias for Interface)
|
||||
func (e *LogEvent) Any(key string, val interface{}) *LogEvent {
|
||||
return e.Interface(key, val)
|
||||
}
|
||||
|
||||
// Strs adds a string slice field to the event
|
||||
func (e *LogEvent) Strs(key string, vals []string) *LogEvent {
|
||||
e.fields = append(e.fields, zap.Strings(key, vals))
|
||||
return e
|
||||
}
|
||||
|
||||
// Log emits the logs for a particular logging event.
|
||||
// The request id associated with the context will be included if presents.
|
||||
func Log(ctx context.Context, e *zerolog.Event, format string, v ...any) {
|
||||
func Log(ctx context.Context, e *LogEvent, format string, v ...any) {
|
||||
id, ok := ctx.Value(ReqIdCtxKey{}).(string)
|
||||
if !ok {
|
||||
e.Msgf(format, v...)
|
||||
@@ -50,3 +124,123 @@ func Log(ctx context.Context, e *zerolog.Event, format string, v ...any) {
|
||||
return fmt.Sprintf("[%s] %s", id, fmt.Sprintf(format, v...))
|
||||
})
|
||||
}
|
||||
|
||||
// Logger methods that mimic zerolog API
|
||||
func (l *Logger) Debug() *LogEvent {
|
||||
return &LogEvent{
|
||||
logger: l.Logger,
|
||||
level: zapcore.DebugLevel,
|
||||
fields: []zap.Field{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Info() *LogEvent {
|
||||
return &LogEvent{
|
||||
logger: l.Logger,
|
||||
level: zapcore.InfoLevel,
|
||||
fields: []zap.Field{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Warn() *LogEvent {
|
||||
return &LogEvent{
|
||||
logger: l.Logger,
|
||||
level: zapcore.WarnLevel,
|
||||
fields: []zap.Field{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Error() *LogEvent {
|
||||
return &LogEvent{
|
||||
logger: l.Logger,
|
||||
level: zapcore.ErrorLevel,
|
||||
fields: []zap.Field{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Fatal() *LogEvent {
|
||||
return &LogEvent{
|
||||
logger: l.Logger,
|
||||
level: zapcore.FatalLevel,
|
||||
fields: []zap.Field{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Notice() *LogEvent {
|
||||
return &LogEvent{
|
||||
logger: l.Logger,
|
||||
level: zapcore.InfoLevel, // zap doesn't have Notice level, use Info
|
||||
fields: []zap.Field{},
|
||||
}
|
||||
}
|
||||
|
||||
// With returns a logger with additional fields
|
||||
func (l *Logger) With() *Logger {
|
||||
return l
|
||||
}
|
||||
|
||||
// Str adds a string field to the logger
|
||||
func (l *Logger) Str(key, val string) *Logger {
|
||||
// Create a new logger with the field added
|
||||
newLogger := l.Logger.With(zap.String(key, val))
|
||||
return &Logger{newLogger}
|
||||
}
|
||||
|
||||
// Err adds an error field to the logger
|
||||
func (l *Logger) Err(err error) *Logger {
|
||||
// Create a new logger with the error field added
|
||||
newLogger := l.Logger.With(zap.Error(err))
|
||||
return &Logger{newLogger}
|
||||
}
|
||||
|
||||
// Any adds an interface field to the logger
|
||||
func (l *Logger) Any(key string, val interface{}) *Logger {
|
||||
// Create a new logger with the field added
|
||||
newLogger := l.Logger.With(zap.Any(key, val))
|
||||
return &Logger{newLogger}
|
||||
}
|
||||
|
||||
// Bool adds a boolean field to the logger
|
||||
func (l *Logger) Bool(key string, val bool) *Logger {
|
||||
// Create a new logger with the field added
|
||||
newLogger := l.Logger.With(zap.Bool(key, val))
|
||||
return &Logger{newLogger}
|
||||
}
|
||||
|
||||
// Msgf logs a formatted message at info level
|
||||
func (l *Logger) Msgf(format string, v ...any) {
|
||||
l.Info().Msgf(format, v...)
|
||||
}
|
||||
|
||||
// Msg logs a message at info level
|
||||
func (l *Logger) Msg(msg string) {
|
||||
l.Info().Msg(msg)
|
||||
}
|
||||
|
||||
// Output returns a logger with the specified output
|
||||
func (l *Logger) Output(w io.Writer) *Logger {
|
||||
// Create a new zap logger with the writer
|
||||
encoderConfig := zap.NewDevelopmentEncoderConfig()
|
||||
encoderConfig.TimeKey = "time"
|
||||
encoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout(time.RFC3339)
|
||||
encoder := zapcore.NewConsoleEncoder(encoderConfig)
|
||||
core := zapcore.NewCore(encoder, zapcore.AddSync(w), zapcore.InfoLevel)
|
||||
newLogger := zap.New(core)
|
||||
return &Logger{newLogger}
|
||||
}
|
||||
|
||||
// GetLogger returns the underlying logger
|
||||
func (l *Logger) GetLogger() *Logger {
|
||||
return l
|
||||
}
|
||||
|
||||
// Write implements io.Writer to allow direct writing to the logger
|
||||
func (l *Logger) Write(p []byte) (n int, err error) {
|
||||
l.Info().Msg(string(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Printf logs a formatted message at info level
|
||||
func (l *Logger) Printf(format string, v ...any) {
|
||||
l.Info().Msgf(format, v...)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user