From c0b39975ceb2a7cffc6d822cca41f3815cb4e27b Mon Sep 17 00:00:00 2001 From: Yash Dev Lamba Date: Mon, 26 Jun 2023 11:15:41 +0530 Subject: [PATCH 1/3] refactor: structured logging input syntax --- log/levels.go | 97 +++++++++++++++++++++++++-------------------------- log/logfmt.go | 87 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 131 insertions(+), 53 deletions(-) diff --git a/log/levels.go b/log/levels.go index e99462f..e5235d3 100644 --- a/log/levels.go +++ b/log/levels.go @@ -6,8 +6,8 @@ import ( "log" "strings" - kitLog "github.com/go-kit/log" kitLevel "github.com/go-kit/log/level" + "github.com/skit-ai/vcore/env" "github.com/skit-ai/vcore/errors" "github.com/skit-ai/vcore/instruments" ) @@ -24,11 +24,38 @@ type Logger struct { level int } +type logWrap interface { + Debug(args ...interface{}) + Info(args ...interface{}) + Warn(args ...interface{}) + Error(err error, args ...interface{}) + + Debugf(format string, args ...interface{}) + Infof(format string, args ...interface{}) + Warnf(format string, args ...interface{}) + Errorf(err error, format string, args ...interface{}) +} + var ( defaultLogger = Logger{WARN} - LogfmtLogger kitLog.Logger + logfmtLogger = logfmtWrapper{} + selectLogger logWrap ) +func init() { + level := env.Int("LOG_LEVEL", 0) + defaultLogger.SetLevel(level) + + format := env.String("LOG_FORMAT", "") + switch format { + case "logfmt": + initLogfmt() + selectLogger = &logfmtLogger + default: + selectLogger = &defaultLogger + } +} + // Prefix based on the log level to be added to every log statement func levelPrefix(level int) string { switch level { @@ -179,44 +206,29 @@ func (logger *Logger) Error(err error, args ...interface{}) { // Methods to log a message using the default logger without a format func Trace(args ...interface{}) { - if LogfmtLogger != nil { + //TODO; verify this + if logfmtLogger.logger != nil { // When logfmt is enabled, level trace becomes debug - kitLevel.Debug(LogfmtLogger).Log(args...) + kitLevel.Debug(*logfmtLogger.logger).Log(args...) return } defaultLogger.Trace(args...) } func Debug(args ...interface{}) { - if LogfmtLogger != nil { - kitLevel.Debug(LogfmtLogger).Log(args...) - return - } - defaultLogger.Debug(args...) + selectLogger.Debug(args...) } func Info(args ...interface{}) { - if LogfmtLogger != nil { - kitLevel.Info(LogfmtLogger).Log(args...) - return - } - defaultLogger.Info(args...) + selectLogger.Info(args...) } func Warn(args ...interface{}) { - if LogfmtLogger != nil { - kitLevel.Warn(LogfmtLogger).Log(args...) - return - } - defaultLogger.Warn(args...) + selectLogger.Warn(args...) } func Error(err error, args ...interface{}) { - if LogfmtLogger != nil { - kitLevel.Error(LogfmtLogger).Log(args...) - return - } - defaultLogger.Error(err, args...) + selectLogger.Error(err, args...) } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -224,44 +236,29 @@ func Error(err error, args ...interface{}) { // TODO: remove format methods func Tracef(format string, args ...interface{}) { - if LogfmtLogger != nil { + //TODO: verify this as well + if logfmtLogger.logger != nil{ // When logfmt is enabled, level trace becomes debug - kitLevel.Debug(LogfmtLogger).Log(fmt.Sprintf(format, args...)) + kitLevel.Debug(*logfmtLogger.logger).Log(fmt.Sprintf(format, args...)) return } defaultLogger.Tracef(format, args...) } func Debugf(format string, args ...interface{}) { - if LogfmtLogger != nil { - kitLevel.Debug(LogfmtLogger).Log(fmt.Sprintf(format, args...)) - return - } - defaultLogger.Debugf(format, args...) + selectLogger.Debugf(format, args...) } func Infof(format string, args ...interface{}) { - if LogfmtLogger != nil { - kitLevel.Info(LogfmtLogger).Log(fmt.Sprintf(format, args...)) - return - } - defaultLogger.Infof(format, args...) + selectLogger.Infof(format, args...) } func Warnf(format string, args ...interface{}) { - if LogfmtLogger != nil { - kitLevel.Warn(LogfmtLogger).Log(fmt.Sprintf(format, args...)) - return - } - defaultLogger.Warnf(format, args...) + selectLogger.Warnf(format, args...) } func Errorf(err error, format string, args ...interface{}) { - if LogfmtLogger != nil { - kitLevel.Error(LogfmtLogger).Log(fmt.Sprintf(format, args...)) - return - } - defaultLogger.Errorf(err, format, args...) + selectLogger.Errorf(err, format, args...) } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -269,22 +266,22 @@ func Errorf(err error, format string, args ...interface{}) { func DebugWithTrace(ctx context.Context, args ...interface{}) { args = append([]any{"trace_id", instruments.ExtractTraceID(ctx).String()}, args...) - kitLevel.Debug(LogfmtLogger).Log(args...) + kitLevel.Debug(*logfmtLogger.logger).Log(args...) } func InfoWithTrace(ctx context.Context, args ...interface{}) { args = append([]any{"trace_id", instruments.ExtractTraceID(ctx).String()}, args...) - kitLevel.Info(LogfmtLogger).Log(args...) + kitLevel.Info(*logfmtLogger.logger).Log(args...) } func WarnWithTrace(ctx context.Context, args ...interface{}) { args = append([]any{"trace_id", instruments.ExtractTraceID(ctx).String()}, args...) - kitLevel.Warn(LogfmtLogger).Log(args...) + kitLevel.Warn(*logfmtLogger.logger).Log(args...) } func ErrorWithTrace(ctx context.Context, args ...interface{}) { args = append([]any{"trace_id", instruments.ExtractTraceID(ctx).String()}, args...) - kitLevel.Error(LogfmtLogger).Log(args...) + kitLevel.Error(*logfmtLogger.logger).Log(args...) } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/log/logfmt.go b/log/logfmt.go index a38facf..a530c5d 100644 --- a/log/logfmt.go +++ b/log/logfmt.go @@ -1,20 +1,27 @@ package log import ( + "context" "fmt" "os" "github.com/go-kit/log" "github.com/go-kit/log/level" + "github.com/skit-ai/vcore/instruments" ) +type logfmtWrapper struct { + logger *log.Logger +} + // InitLogfmtLogger initialises a logfmt logger from go-kit -func InitLogfmtLogger() { +func initLogfmt() { logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) // Using legacy logger's level for level filtering logger = level.NewFilter(logger, LevelFilter(defaultLogger.level)) logger = log.With(logger, "ts", log.DefaultTimestampUTC) - LogfmtLogger = log.With(logger, "caller", log.Caller(4)) + logger = log.With(logger, "caller", log.Caller(5)) + logfmtLogger = logfmtWrapper{&logger} } // CheckFatal prints an error and exits with error code 1 if err is non-nil. @@ -23,7 +30,7 @@ func CheckFatal(location string, err error) { return } - logger := level.Error(LogfmtLogger) + logger := level.Error(*logfmtLogger.logger) if location != "" { logger = log.With(logger, "msg", "error "+location) } @@ -49,3 +56,77 @@ func LevelFilter(l int) level.Option { return level.AllowAll() } } + +// TODO: better naming +func mapToArgs(m map[string]interface{}) []interface{} { + var args []interface{} + for k, v := range m { + args = append(args, k) + args = append(args, v) + } + return args +} + +func (lw *logfmtWrapper) Info(args ...interface{}) { + level.Info(*lw.logger).Log("msg", args[0]) +} + +func (lw *logfmtWrapper) Warn(args ...interface{}) { + level.Warn(*lw.logger).Log("msg", args[0]) +} + +func (lw *logfmtWrapper) Debug(args ...interface{}) { + level.Debug(*lw.logger).Log("msg", args[0]) +} + +func (lw *logfmtWrapper) Error(err error, args ...interface{}) { + level.Error(*lw.logger).Log("msg", args) +} + +func (lw *logfmtWrapper) Infof(format string, args ...interface{}) { + level.Info(*lw.logger).Log("msg", fmt.Sprintf(format, args...)) +} + +func (lw *logfmtWrapper) Warnf(format string, args ...interface{}) { + level.Warn(*lw.logger).Log("msg", fmt.Sprintf(format, args...)) +} + +func (lw *logfmtWrapper) Debugf(format string, args ...interface{}) { + level.Debug(*lw.logger).Log("msg", fmt.Sprintf(format, args...)) +} + +func (lw *logfmtWrapper) Errorf(err error, format string, args ...interface{}) { + level.Error(*lw.logger).Log("msg", fmt.Sprintf(format, args...)) +} + +func (lw *logfmtWrapper) WithTraceId(ctx context.Context) *logfmtWrapper { + traceId := instruments.ExtractTraceID(ctx) + *lw.logger = log.With(*lw.logger, "trace_id", traceId) + return lw +} + +func (lw *logfmtWrapper) WithFields(fields map[string]interface{}) *logfmtWrapper { + fieldArgs := mapToArgs(fields) + *lw.logger = log.With(*lw.logger, fieldArgs...) + return lw +} + +func WithTraceId(ctx context.Context) *logfmtWrapper { + return logfmtLogger.WithTraceId(ctx) +} + +func WithFields(fields map[string]interface{}) *logfmtWrapper { + return logfmtLogger.WithFields(fields) +} + +func WithContext(ctx context.Context, fields map[string]interface{}) *logfmtWrapper { + if ctx == nil { + return logfmtLogger.WithFields(fields) + } + + if fields == nil { + return logfmtLogger.WithTraceId(ctx) + } + + return logfmtLogger.WithFields(fields).WithTraceId(ctx) +} From ae46e316576da4b473536ad0ace683076dead38d Mon Sep 17 00:00:00 2001 From: Yash Dev Lamba Date: Wed, 28 Jun 2023 01:36:48 +0530 Subject: [PATCH 2/3] refactor: move logfmt support to a new package slog --- log/levels.go | 88 +++--------------------- log/logfmt.go | 132 ------------------------------------ log/slog/slog.go | 172 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+), 211 deletions(-) delete mode 100644 log/logfmt.go create mode 100644 log/slog/slog.go diff --git a/log/levels.go b/log/levels.go index e5235d3..f44668d 100644 --- a/log/levels.go +++ b/log/levels.go @@ -1,15 +1,11 @@ package log import ( - "context" "fmt" "log" "strings" - kitLevel "github.com/go-kit/log/level" - "github.com/skit-ai/vcore/env" "github.com/skit-ai/vcore/errors" - "github.com/skit-ai/vcore/instruments" ) const ( @@ -24,37 +20,7 @@ type Logger struct { level int } -type logWrap interface { - Debug(args ...interface{}) - Info(args ...interface{}) - Warn(args ...interface{}) - Error(err error, args ...interface{}) - - Debugf(format string, args ...interface{}) - Infof(format string, args ...interface{}) - Warnf(format string, args ...interface{}) - Errorf(err error, format string, args ...interface{}) -} - -var ( - defaultLogger = Logger{WARN} - logfmtLogger = logfmtWrapper{} - selectLogger logWrap -) - -func init() { - level := env.Int("LOG_LEVEL", 0) - defaultLogger.SetLevel(level) - - format := env.String("LOG_FORMAT", "") - switch format { - case "logfmt": - initLogfmt() - selectLogger = &logfmtLogger - default: - selectLogger = &defaultLogger - } -} +var defaultLogger = Logger{WARN} // Prefix based on the log level to be added to every log statement func levelPrefix(level int) string { @@ -206,82 +172,46 @@ func (logger *Logger) Error(err error, args ...interface{}) { // Methods to log a message using the default logger without a format func Trace(args ...interface{}) { - //TODO; verify this - if logfmtLogger.logger != nil { - // When logfmt is enabled, level trace becomes debug - kitLevel.Debug(*logfmtLogger.logger).Log(args...) - return - } defaultLogger.Trace(args...) } func Debug(args ...interface{}) { - selectLogger.Debug(args...) + defaultLogger.Debug(args...) } func Info(args ...interface{}) { - selectLogger.Info(args...) + defaultLogger.Info(args...) } func Warn(args ...interface{}) { - selectLogger.Warn(args...) + defaultLogger.Warn(args...) } func Error(err error, args ...interface{}) { - selectLogger.Error(err, args...) + defaultLogger.Error(err, args...) } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Methods to log messages using the default logger with a format -// TODO: remove format methods func Tracef(format string, args ...interface{}) { - //TODO: verify this as well - if logfmtLogger.logger != nil{ - // When logfmt is enabled, level trace becomes debug - kitLevel.Debug(*logfmtLogger.logger).Log(fmt.Sprintf(format, args...)) - return - } defaultLogger.Tracef(format, args...) } func Debugf(format string, args ...interface{}) { - selectLogger.Debugf(format, args...) + defaultLogger.Debugf(format, args...) } func Infof(format string, args ...interface{}) { - selectLogger.Infof(format, args...) + defaultLogger.Infof(format, args...) } func Warnf(format string, args ...interface{}) { - selectLogger.Warnf(format, args...) + defaultLogger.Warnf(format, args...) } func Errorf(err error, format string, args ...interface{}) { - selectLogger.Errorf(err, format, args...) -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Methods to log messages using the logfomt logger with a trace_id - -func DebugWithTrace(ctx context.Context, args ...interface{}) { - args = append([]any{"trace_id", instruments.ExtractTraceID(ctx).String()}, args...) - kitLevel.Debug(*logfmtLogger.logger).Log(args...) -} - -func InfoWithTrace(ctx context.Context, args ...interface{}) { - args = append([]any{"trace_id", instruments.ExtractTraceID(ctx).String()}, args...) - kitLevel.Info(*logfmtLogger.logger).Log(args...) -} - -func WarnWithTrace(ctx context.Context, args ...interface{}) { - args = append([]any{"trace_id", instruments.ExtractTraceID(ctx).String()}, args...) - kitLevel.Warn(*logfmtLogger.logger).Log(args...) -} - -func ErrorWithTrace(ctx context.Context, args ...interface{}) { - args = append([]any{"trace_id", instruments.ExtractTraceID(ctx).String()}, args...) - kitLevel.Error(*logfmtLogger.logger).Log(args...) + defaultLogger.Errorf(err, format, args...) } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/log/logfmt.go b/log/logfmt.go deleted file mode 100644 index a530c5d..0000000 --- a/log/logfmt.go +++ /dev/null @@ -1,132 +0,0 @@ -package log - -import ( - "context" - "fmt" - "os" - - "github.com/go-kit/log" - "github.com/go-kit/log/level" - "github.com/skit-ai/vcore/instruments" -) - -type logfmtWrapper struct { - logger *log.Logger -} - -// InitLogfmtLogger initialises a logfmt logger from go-kit -func initLogfmt() { - logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) - // Using legacy logger's level for level filtering - logger = level.NewFilter(logger, LevelFilter(defaultLogger.level)) - logger = log.With(logger, "ts", log.DefaultTimestampUTC) - logger = log.With(logger, "caller", log.Caller(5)) - logfmtLogger = logfmtWrapper{&logger} -} - -// CheckFatal prints an error and exits with error code 1 if err is non-nil. -func CheckFatal(location string, err error) { - if err == nil { - return - } - - logger := level.Error(*logfmtLogger.logger) - if location != "" { - logger = log.With(logger, "msg", "error "+location) - } - // %+v gets the stack trace from errors using github.com/pkg/errors - errStr := fmt.Sprintf("%+v", err) - fmt.Fprintln(os.Stderr, errStr) - - logger.Log("err", errStr) - os.Exit(1) -} - -func LevelFilter(l int) level.Option { - switch l { - case DEBUG: - return level.AllowDebug() - case INFO: - return level.AllowInfo() - case WARN: - return level.AllowWarn() - case ERROR: - return level.AllowError() - default: - return level.AllowAll() - } -} - -// TODO: better naming -func mapToArgs(m map[string]interface{}) []interface{} { - var args []interface{} - for k, v := range m { - args = append(args, k) - args = append(args, v) - } - return args -} - -func (lw *logfmtWrapper) Info(args ...interface{}) { - level.Info(*lw.logger).Log("msg", args[0]) -} - -func (lw *logfmtWrapper) Warn(args ...interface{}) { - level.Warn(*lw.logger).Log("msg", args[0]) -} - -func (lw *logfmtWrapper) Debug(args ...interface{}) { - level.Debug(*lw.logger).Log("msg", args[0]) -} - -func (lw *logfmtWrapper) Error(err error, args ...interface{}) { - level.Error(*lw.logger).Log("msg", args) -} - -func (lw *logfmtWrapper) Infof(format string, args ...interface{}) { - level.Info(*lw.logger).Log("msg", fmt.Sprintf(format, args...)) -} - -func (lw *logfmtWrapper) Warnf(format string, args ...interface{}) { - level.Warn(*lw.logger).Log("msg", fmt.Sprintf(format, args...)) -} - -func (lw *logfmtWrapper) Debugf(format string, args ...interface{}) { - level.Debug(*lw.logger).Log("msg", fmt.Sprintf(format, args...)) -} - -func (lw *logfmtWrapper) Errorf(err error, format string, args ...interface{}) { - level.Error(*lw.logger).Log("msg", fmt.Sprintf(format, args...)) -} - -func (lw *logfmtWrapper) WithTraceId(ctx context.Context) *logfmtWrapper { - traceId := instruments.ExtractTraceID(ctx) - *lw.logger = log.With(*lw.logger, "trace_id", traceId) - return lw -} - -func (lw *logfmtWrapper) WithFields(fields map[string]interface{}) *logfmtWrapper { - fieldArgs := mapToArgs(fields) - *lw.logger = log.With(*lw.logger, fieldArgs...) - return lw -} - -func WithTraceId(ctx context.Context) *logfmtWrapper { - return logfmtLogger.WithTraceId(ctx) -} - -func WithFields(fields map[string]interface{}) *logfmtWrapper { - return logfmtLogger.WithFields(fields) -} - -func WithContext(ctx context.Context, fields map[string]interface{}) *logfmtWrapper { - if ctx == nil { - return logfmtLogger.WithFields(fields) - } - - if fields == nil { - return logfmtLogger.WithTraceId(ctx) - } - - return logfmtLogger.WithFields(fields).WithTraceId(ctx) -} diff --git a/log/slog/slog.go b/log/slog/slog.go new file mode 100644 index 0000000..7168542 --- /dev/null +++ b/log/slog/slog.go @@ -0,0 +1,172 @@ +package slog + +import ( + "context" + "fmt" + "os" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" + "github.com/skit-ai/vcore/env" + "github.com/skit-ai/vcore/instruments" +) + +type loggerWrapper struct { + logger log.Logger +} + +var defaultLoggerWrapper *loggerWrapper + +func init() { + logLevel := env.String("LOG_LEVEL", "info") + defaultLoggerWrapper = newloggerWrapper(logLevel) +} + +func newloggerWrapper(logLevel string) *loggerWrapper { + logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) + logger = level.NewFilter(logger, levelFilter(logLevel)) + logger = log.With(logger, "ts", log.DefaultTimestamp) + logger = log.With(logger, "caller", log.Caller(4)) + + return &loggerWrapper{ + logger: logger, + } +} + +func (l *loggerWrapper) Info(msg string, args ...any) { + level.Info(log.With(l.logger, "msg", msg)).Log(args...) +} + +func (l *loggerWrapper) Warn(msg string, args ...any) { + level.Warn(log.With(l.logger, "msg", msg)).Log(args...) +} + +func (l *loggerWrapper) Debug(msg string, args ...any) { + level.Debug(log.With(defaultLoggerWrapper.logger, "msg", msg)).Log(args...) +} + +func (l *loggerWrapper) Error(err error, msg string, args ...any) { + if err == nil { + level.Error(log.With(l.logger, "msg", msg)).Log(args...) + return + } + + if msg == "" { + level.Error(log.With(l.logger, "err", err)).Log(args...) + return + } + + level.Error(log.With(l.logger, "msg", msg, "err", err)).Log(args...) +} + +func (l *loggerWrapper) Infof(format string, args ...any) { + level.Info(l.logger).Log("msg", fmt.Sprintf(format, args...)) +} + +func (l *loggerWrapper) Warnf(format string, args ...any) { + level.Warn(l.logger).Log("msg", fmt.Sprintf(format, args...)) +} + +func (l *loggerWrapper) Debugf(format string, args ...any) { + level.Debug(l.logger).Log("msg", fmt.Sprintf(format, args...)) +} + +func (l *loggerWrapper) Errorf(err error, format string, args ...any) { + level.Error(l.logger).Log("err", err.Error(), "msg", fmt.Sprintf(format, args...)) +} + +func (l *loggerWrapper) WithTraceId(ctx context.Context) *loggerWrapper { + traceId := instruments.ExtractTraceID(ctx) + logger := log.With(l.logger, "trace_id", traceId) + + return &loggerWrapper{ + logger: logger, + } +} + +func (l *loggerWrapper) WithFields(fields map[string]any) *loggerWrapper { + fieldArgs := mapToSlice(fields) + logger := log.With(l.logger, fieldArgs...) + + return &loggerWrapper{ + logger: logger, + } +} + +func Info(msg string, args ...any) { + level.Info(log.With(defaultLoggerWrapper.logger, "msg", msg)).Log(args...) +} + +func Warn(msg string, args ...any) { + level.Warn(log.With(defaultLoggerWrapper.logger, "msg", msg)).Log(args...) +} + +func Debug(msg string, args ...any) { + level.Debug(log.With(defaultLoggerWrapper.logger, "msg", msg)).Log(args...) +} + +func Error(err error, msg string, args ...any) { + if err == nil { + level.Error(log.With(defaultLoggerWrapper.logger, "msg", msg)).Log(args...) + return + } + + if msg == "" { + level.Error(log.With(defaultLoggerWrapper.logger, "err", err)).Log(args...) + return + } + + level.Error(log.With(defaultLoggerWrapper.logger, "msg", msg, "err", err)).Log(args...) +} + +func Infof(format string, args ...any) { + level.Info(defaultLoggerWrapper.logger).Log("msg", fmt.Sprintf(format, args...)) +} + +func Warnf(format string, args ...any) { + level.Warn(defaultLoggerWrapper.logger).Log("msg", fmt.Sprintf(format, args...)) +} + +func Debugf(format string, args ...any) { + level.Debug(defaultLoggerWrapper.logger).Log("msg", fmt.Sprintf(format, args...)) +} + +func Errorf(err error, format string, args ...any) { + level.Error(defaultLoggerWrapper.logger).Log("err", err.Error(), "msg", fmt.Sprintf(format, args...)) +} + +func WithFields(fields map[string]any) *loggerWrapper { + return defaultLoggerWrapper.WithFields(fields) +} + +func WithTraceId(ctx context.Context) *loggerWrapper { + return defaultLoggerWrapper.WithTraceId(ctx) +} + +func levelFilter(logLevel string) level.Option { + switch logLevel { + case "debug": + return level.AllowDebug() + case "info": + return level.AllowInfo() + case "warn": + return level.AllowWarn() + case "error": + return level.AllowError() + // Invalid or no logLevel means all levels are allowed to be logged + default: + return level.AllowAll() + } +} + +func mapToSlice(m map[string]any) []any { + var args []any + for k, v := range m { + args = append(args, k, v) + } + return args +} + +func GetLogger() log.Logger { + return defaultLoggerWrapper.logger +} From 62fd5f276a8326a7478c0f917d6eb95fc9cf8dec Mon Sep 17 00:00:00 2001 From: Yash Dev Lamba Date: Thu, 6 Jul 2023 13:00:45 +0530 Subject: [PATCH 3/3] add: docstrings to exported methods; refactor: variable names and place --- log/slog/slog.go | 136 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 92 insertions(+), 44 deletions(-) diff --git a/log/slog/slog.go b/log/slog/slog.go index 7168542..2eb4acd 100644 --- a/log/slog/slog.go +++ b/log/slog/slog.go @@ -15,6 +15,8 @@ type loggerWrapper struct { logger log.Logger } +const defaultMsgKey = "msg" +const defaultErrKey = "error" var defaultLoggerWrapper *loggerWrapper func init() { @@ -33,48 +35,94 @@ func newloggerWrapper(logLevel string) *loggerWrapper { } } +func levelFilter(logLevel string) level.Option { + switch logLevel { + case "debug": + return level.AllowDebug() + case "info": + return level.AllowInfo() + case "warn": + return level.AllowWarn() + case "error": + return level.AllowError() + // Invalid or no logLevel means all levels are allowed to be logged + default: + return level.AllowAll() + } +} + +func mapToSlice(m map[string]any) []any { + var args []any + for k, v := range m { + args = append(args, k, v) + } + return args +} + + +// Info logs a line with level info using the loggerWrapper instance. func (l *loggerWrapper) Info(msg string, args ...any) { - level.Info(log.With(l.logger, "msg", msg)).Log(args...) + level.Info(log.With(l.logger, defaultMsgKey, msg)).Log(args...) } +// Warn logs a line with level warn using the loggerWrapper instance. func (l *loggerWrapper) Warn(msg string, args ...any) { - level.Warn(log.With(l.logger, "msg", msg)).Log(args...) + level.Warn(log.With(l.logger, defaultMsgKey, msg)).Log(args...) } +// Debug logs a line with level debug using a loggerWrapper instance. func (l *loggerWrapper) Debug(msg string, args ...any) { - level.Debug(log.With(defaultLoggerWrapper.logger, "msg", msg)).Log(args...) + level.Debug(log.With(defaultLoggerWrapper.logger, defaultMsgKey, msg)).Log(args...) } +// Error logs a line with level error using a loggerWrapper instance. +// If err is not nil it adds only the msg string or vice-versa. Otherwise adds both. func (l *loggerWrapper) Error(err error, msg string, args ...any) { if err == nil { - level.Error(log.With(l.logger, "msg", msg)).Log(args...) + level.Error(log.With(l.logger, defaultMsgKey, msg)).Log(args...) return } if msg == "" { - level.Error(log.With(l.logger, "err", err)).Log(args...) + level.Error(log.With(l.logger, defaultErrKey, err.Error())).Log(args...) return } - level.Error(log.With(l.logger, "msg", msg, "err", err)).Log(args...) + level.Error(log.With(l.logger, defaultMsgKey, msg, defaultErrKey, err.Error())).Log(args...) } +// Infof logs a format line with level info using the loggerWrapper instance. func (l *loggerWrapper) Infof(format string, args ...any) { - level.Info(l.logger).Log("msg", fmt.Sprintf(format, args...)) + level.Info(l.logger).Log(defaultMsgKey, fmt.Sprintf(format, args...)) } +// Warnf logs a format line with level warn using the loggerWrapper instance. func (l *loggerWrapper) Warnf(format string, args ...any) { - level.Warn(l.logger).Log("msg", fmt.Sprintf(format, args...)) + level.Warn(l.logger).Log(defaultMsgKey, fmt.Sprintf(format, args...)) } +// Debugf logs a format line with level debug using the loggerWrapper instance. func (l *loggerWrapper) Debugf(format string, args ...any) { - level.Debug(l.logger).Log("msg", fmt.Sprintf(format, args...)) + level.Debug(l.logger).Log(defaultMsgKey, fmt.Sprintf(format, args...)) } +// Errorf logs a format line with level error using a loggerWrapper instance. +// If err is not nil it adds only the msg string or vice-versa. Otherwise adds both. func (l *loggerWrapper) Errorf(err error, format string, args ...any) { - level.Error(l.logger).Log("err", err.Error(), "msg", fmt.Sprintf(format, args...)) + if err == nil { + level.Error(l.logger).Log(defaultMsgKey, fmt.Sprintf(format, args...)) + return + } + + if format == "" { + level.Error(l.logger).Log(defaultErrKey, err.Error()) + return + } + + level.Error(l.logger).Log(defaultMsgKey, fmt.Sprintf(format, args...), defaultErrKey, err.Error()) } +// WithTraceId returns a pointer to updated loggerWrapper with trace_id attached to the logger. func (l *loggerWrapper) WithTraceId(ctx context.Context) *loggerWrapper { traceId := instruments.ExtractTraceID(ctx) logger := log.With(l.logger, "trace_id", traceId) @@ -84,6 +132,7 @@ func (l *loggerWrapper) WithTraceId(ctx context.Context) *loggerWrapper { } } +// WithFields returns a pointer to updated loggerWrapper with custom fields attached to the logger. func (l *loggerWrapper) WithFields(fields map[string]any) *loggerWrapper { fieldArgs := mapToSlice(fields) logger := log.With(l.logger, fieldArgs...) @@ -93,80 +142,79 @@ func (l *loggerWrapper) WithFields(fields map[string]any) *loggerWrapper { } } +// Info logs a line with level Info. func Info(msg string, args ...any) { - level.Info(log.With(defaultLoggerWrapper.logger, "msg", msg)).Log(args...) + level.Info(log.With(defaultLoggerWrapper.logger, defaultMsgKey, msg)).Log(args...) } +// Warn logs a line with level warn. func Warn(msg string, args ...any) { - level.Warn(log.With(defaultLoggerWrapper.logger, "msg", msg)).Log(args...) + level.Warn(log.With(defaultLoggerWrapper.logger, defaultMsgKey, msg)).Log(args...) } +// Debug logs a line with level debug. func Debug(msg string, args ...any) { - level.Debug(log.With(defaultLoggerWrapper.logger, "msg", msg)).Log(args...) + level.Debug(log.With(defaultLoggerWrapper.logger, defaultMsgKey, msg)).Log(args...) } +// Error logs a line with level error. +// If err is not nil it adds only the msg string or vice-versa. Otherwise adds both. func Error(err error, msg string, args ...any) { if err == nil { - level.Error(log.With(defaultLoggerWrapper.logger, "msg", msg)).Log(args...) + level.Error(log.With(defaultLoggerWrapper.logger, defaultMsgKey, msg)).Log(args...) return } if msg == "" { - level.Error(log.With(defaultLoggerWrapper.logger, "err", err)).Log(args...) + level.Error(log.With(defaultLoggerWrapper.logger, defaultErrKey, err.Error())).Log(args...) return } - level.Error(log.With(defaultLoggerWrapper.logger, "msg", msg, "err", err)).Log(args...) + level.Error(log.With(defaultLoggerWrapper.logger, defaultMsgKey, msg, defaultErrKey, err.Error())).Log(args...) } +// Infof logs a format line with level info. func Infof(format string, args ...any) { - level.Info(defaultLoggerWrapper.logger).Log("msg", fmt.Sprintf(format, args...)) + level.Info(defaultLoggerWrapper.logger).Log(defaultMsgKey, fmt.Sprintf(format, args...)) } +// Warnf logs a format line with level warn. func Warnf(format string, args ...any) { - level.Warn(defaultLoggerWrapper.logger).Log("msg", fmt.Sprintf(format, args...)) + level.Warn(defaultLoggerWrapper.logger).Log(defaultMsgKey, fmt.Sprintf(format, args...)) } +// Debugf logs a format line with level debug. func Debugf(format string, args ...any) { - level.Debug(defaultLoggerWrapper.logger).Log("msg", fmt.Sprintf(format, args...)) + level.Debug(defaultLoggerWrapper.logger).Log(defaultMsgKey, fmt.Sprintf(format, args...)) } +// Errorf logs a format line with level error. +// If err is not nil it adds only the msg string or vice-versa. Otherwise adds both. func Errorf(err error, format string, args ...any) { - level.Error(defaultLoggerWrapper.logger).Log("err", err.Error(), "msg", fmt.Sprintf(format, args...)) + if err == nil { + level.Error(defaultLoggerWrapper.logger).Log(defaultMsgKey, fmt.Sprintf(format, args...)) + return + } + + if format == "" { + level.Error(defaultLoggerWrapper.logger).Log(defaultErrKey, err.Error()) + return + } + + level.Error(defaultLoggerWrapper.logger).Log(defaultMsgKey, fmt.Sprintf(format, args...), defaultErrKey, err.Error()) } +// WithFields returns WithFields using the defaultLoggerWrapper. func WithFields(fields map[string]any) *loggerWrapper { return defaultLoggerWrapper.WithFields(fields) } +// WithTraceId returns WithTraceId using the defaultLoggerWrapper. func WithTraceId(ctx context.Context) *loggerWrapper { return defaultLoggerWrapper.WithTraceId(ctx) } -func levelFilter(logLevel string) level.Option { - switch logLevel { - case "debug": - return level.AllowDebug() - case "info": - return level.AllowInfo() - case "warn": - return level.AllowWarn() - case "error": - return level.AllowError() - // Invalid or no logLevel means all levels are allowed to be logged - default: - return level.AllowAll() - } -} - -func mapToSlice(m map[string]any) []any { - var args []any - for k, v := range m { - args = append(args, k, v) - } - return args -} - +// GetLogger returns the default instance of logfmt logger func GetLogger() log.Logger { return defaultLoggerWrapper.logger }