From a349126a65a5d1ca7c36a14cbbf779baa20cf47e Mon Sep 17 00:00:00 2001 From: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com> Date: Fri, 15 Nov 2024 10:17:42 +0000 Subject: [PATCH] refactor: implement level string function as method Signed-off-by: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com> --- examples/extension/main.go | 4 +-- internal/logger/core.go | 17 +++++++---- internal/logger/extensions_test.go | 20 ++++++------- internal/logger/level.go | 46 +++++++++++++++--------------- internal/logger/level_test.go | 4 +-- internal/logger/utils.go | 17 +++++------ internal/logger/utils_test.go | 4 +-- logger/logger.go | 27 ++++++++++++++++-- 8 files changed, 84 insertions(+), 55 deletions(-) diff --git a/examples/extension/main.go b/examples/extension/main.go index ef61d3a..a1180e5 100644 --- a/examples/extension/main.go +++ b/examples/extension/main.go @@ -13,7 +13,7 @@ type Logger interface { Success(msg string, args ...any) } -const LevelSuccess = slog.Level(1) +const LevelSuccess = logger.Level(1) type loggerExtension struct { logger.Provider @@ -46,7 +46,7 @@ func NewLogger() Logger { func replaceAttr(_ []string, a slog.Attr) slog.Attr { if a.Key == slog.LevelKey { l := a.Value.Any().(slog.Level) - if l == LevelSuccess { + if l == slog.Level(LevelSuccess) { a.Value = slog.StringValue("SUCCESS") } } diff --git a/internal/logger/core.go b/internal/logger/core.go index c693418..7a6c893 100644 --- a/internal/logger/core.go +++ b/internal/logger/core.go @@ -89,12 +89,12 @@ type Provider interface { // into an Attr. // - Otherwise, the argument is treated as a value with key "!BADKEY". Log(ctx context.Context, level Level, msg string, args ...any) - // LogAttrs is a more efficient version of [Logger].Log that accepts only Attrs. + // LogAttrs is a more efficient version of [Provider.Log] that accepts only Attrs. LogAttrs(ctx context.Context, level Level, msg string, attrs ...slog.Attr) // Handler returns the [slog.Handler] that the Logger emits log records to. Handler() slog.Handler - // Enabled reports whether the [Logger] emits log records at the given context and level. + // Enabled reports whether the [Provider] emits log records at the given context and level. Enabled(ctx context.Context, level Level) bool // ToSlog returns the underlying [slog.Logger]. @@ -157,12 +157,17 @@ func (l *logger) WithGroup(name string) Provider { // Log emits a log record with the current time and the given level and message. func (l *logger) Log(ctx context.Context, level Level, msg string, a ...any) { - l.Logger.Log(ctx, level, msg, a...) + l.Logger.Log(ctx, slog.Level(level), msg, a...) } -// Logf emits a log record with the current time and the given level, message, and attributes. +// LogAttrs is a more efficient version of [Provider.Log] that accepts only Attrs. func (l *logger) LogAttrs(ctx context.Context, level Level, msg string, attrs ...slog.Attr) { - l.Logger.LogAttrs(ctx, level, msg, attrs...) + l.Logger.LogAttrs(ctx, slog.Level(level), msg, attrs...) +} + +// Enabled reports whether the [Provider] emits log records at the given context and level. +func (l *logger) Enabled(ctx context.Context, level Level) bool { + return l.Logger.Enabled(ctx, slog.Level(level)) } // logAttrs emits a log record with the current time and the given level, message, and attributes. @@ -177,7 +182,7 @@ func (l *logger) logAttrs(ctx context.Context, level Level, msg string, a ...any const skip = 3 var pcs [1]uintptr _ = runtime.Callers(skip, pcs[:]) - r := slog.NewRecord(time.Now(), level, msg, pcs[0]) + r := slog.NewRecord(time.Now(), slog.Level(level), msg, pcs[0]) r.Add(a...) if ctx == nil { ctx = context.Background() diff --git a/internal/logger/extensions_test.go b/internal/logger/extensions_test.go index 7073089..e067ba4 100644 --- a/internal/logger/extensions_test.go +++ b/internal/logger/extensions_test.go @@ -39,8 +39,8 @@ func TestLogger_LevelExtensions(t *testing.T) { handler: test.MockHandler{ HandleFunc: func(ctx context.Context, r slog.Record) error { level := LevelDebug - if r.Level != level { - t.Errorf("Expected level to be [%s], got [%s]", getLevelString(level), r.Level) + if r.Level != slog.Level(level) { + t.Errorf("Expected level to be [%s], got [%s]", level, r.Level) } if r.NumAttrs() != 0 { t.Errorf("Expected 0 attributes, got %d", r.NumAttrs()) @@ -72,8 +72,8 @@ func TestLogger_LevelExtensions(t *testing.T) { handler: test.MockHandler{ HandleFunc: func(ctx context.Context, r slog.Record) error { level := LevelInfo - if r.Level != level { - t.Errorf("Expected level to be [%s], got [%s]", getLevelString(level), r.Level) + if r.Level != slog.Level(level) { + t.Errorf("Expected level to be [%s], got [%s]", level, r.Level) } if r.NumAttrs() != 0 { t.Errorf("Expected 0 attributes, got %d", r.NumAttrs()) @@ -105,8 +105,8 @@ func TestLogger_LevelExtensions(t *testing.T) { handler: test.MockHandler{ HandleFunc: func(ctx context.Context, r slog.Record) error { level := LevelWarn - if r.Level != level { - t.Errorf("Expected level to be [%s], got [%s]", getLevelString(level), r.Level) + if r.Level != slog.Level(level) { + t.Errorf("Expected level to be [%s], got [%s]", level, r.Level) } if r.NumAttrs() != 0 { t.Errorf("Expected 0 attributes, got %d", r.NumAttrs()) @@ -138,8 +138,8 @@ func TestLogger_LevelExtensions(t *testing.T) { handler: test.MockHandler{ HandleFunc: func(ctx context.Context, r slog.Record) error { level := LevelError - if r.Level != level { - t.Errorf("Expected level to be [%s], got [%s]", getLevelString(level), r.Level) + if r.Level != slog.Level(level) { + t.Errorf("Expected level to be [%s], got [%s]", level, r.Level) } if r.NumAttrs() != 0 { t.Errorf("Expected 0 attributes, got %d", r.NumAttrs()) @@ -337,8 +337,8 @@ func TestLogger_CustomLevels(t *testing.T) { func assertRecordLevel(t *testing.T, r *slog.Record, level Level, wantAttrs bool) error { t.Helper() - if r.Level != level { - t.Errorf("Expected level to be [%s], got [%s]", getLevelString(level), r.Level) + if r.Level != slog.Level(level) { + t.Errorf("Expected level to be [%s], got [%s]", level, r.Level) } if !wantAttrs && r.NumAttrs() != 0 { t.Errorf("Expected 0 attributes, got %d", r.NumAttrs()) diff --git a/internal/logger/level.go b/internal/logger/level.go index ecebcc1..55ae12c 100644 --- a/internal/logger/level.go +++ b/internal/logger/level.go @@ -5,18 +5,19 @@ import ( "strings" ) -// Level is the type of log levels. -type Level = slog.Level +// Level is a custom type for log levels. +type Level slog.Level +// Log levels. const ( - LevelTrace Level = slog.Level(-8) - LevelDebug Level = slog.LevelDebug - LevelInfo Level = slog.LevelInfo - LevelNotice Level = slog.Level(2) - LevelWarn Level = slog.LevelWarn - LevelError Level = slog.LevelError - LevelPanic Level = slog.Level(12) - LevelFatal Level = slog.Level(16) + LevelTrace = Level(-8) + LevelDebug = Level(slog.LevelDebug) + LevelInfo = Level(slog.LevelInfo) + LevelNotice = Level(slog.Level(2)) + LevelWarn = Level(slog.LevelWarn) + LevelError = Level(slog.LevelError) + LevelPanic = Level(slog.Level(12)) + LevelFatal = Level(slog.Level(16)) ) // LevelNames is a map of log levels to their respective names. @@ -43,30 +44,29 @@ var LevelColors = map[Level]string{ LevelFatal: "160", // FATAL - Dark Red } -// getLevel returns the integer value of the given level string. -// If the level is not recognized, it returns LevelInfo. -func getLevel(level string) int { +// newLevel returns the log level based on the provided string. +// Returns [LevelInfo] if the level is not recognized. +func newLevel(level string) Level { switch strings.ToUpper(level) { case "TRACE": - return int(LevelTrace) + return LevelTrace case "DEBUG": - return int(LevelDebug) + return LevelDebug case "INFO": - return int(LevelInfo) + return LevelInfo case "NOTICE": - return int(LevelNotice) + return LevelNotice case "WARN", "WARNING": - return int(LevelWarn) + return LevelWarn case "ERROR": - return int(LevelError) + return LevelError default: - return int(LevelInfo) + return LevelInfo } } -// getLevelString returns the string value of the given level. -func getLevelString(level Level) string { - if s, ok := LevelNames[level]; ok { +func (l Level) String() string { + if s, ok := LevelNames[l]; ok { return s } return "UNKNOWN" diff --git a/internal/logger/level_test.go b/internal/logger/level_test.go index 2107d62..f881750 100644 --- a/internal/logger/level_test.go +++ b/internal/logger/level_test.go @@ -23,8 +23,8 @@ func TestGetLevel(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := getLevel(tt.input) - if got != int(tt.want) { + got := newLevel(tt.input) + if got != tt.want { t.Errorf("getLevel(%s) = %v, want %v", tt.input, got, tt.want) } }) diff --git a/internal/logger/utils.go b/internal/logger/utils.go index 5b99595..3fc8a7b 100644 --- a/internal/logger/utils.go +++ b/internal/logger/utils.go @@ -83,7 +83,7 @@ func Middleware(ctx context.Context) func(http.Handler) http.Handler { } } -// ToSlog returns the underlying slog.Logger. +// ToSlog returns the underlying [slog.Logger]. func (l *logger) ToSlog() *slog.Logger { if l.Logger == nil { return slog.New(newHandler()) @@ -92,7 +92,7 @@ func (l *logger) ToSlog() *slog.Logger { return l.Logger } -// FromSlog returns a new Logger instance based on the provided slog.Logger. +// FromSlog returns a new Logger instance based on the provided [slog.Logger]. func FromSlog(l *slog.Logger) Provider { if l == nil { return NewLogger() @@ -123,7 +123,7 @@ func newBaseHandler(o Options) slog.Handler { if strings.EqualFold(o.Format, "TEXT") { log := clog.NewWithOptions(os.Stderr, clog.Options{ TimeFormat: time.Kitchen, - Level: clog.Level(getLevel(o.Level)), + Level: clog.Level(newLevel(o.Level)), ReportTimestamp: true, ReportCaller: true, }) @@ -133,7 +133,7 @@ func newBaseHandler(o Options) slog.Handler { return slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{ AddSource: true, - Level: Level(getLevel(o.Level)), + Level: slog.Level(newLevel(o.Level)), ReplaceAttr: replaceAttr, }) } @@ -142,11 +142,12 @@ func newBaseHandler(o Options) slog.Handler { func newCustomStyles() *clog.Styles { styles := clog.DefaultStyles() + const maxWidth = 4 for level, color := range LevelColors { styles.Levels[clog.Level(int(level))] = lipgloss.NewStyle(). - SetString(getLevelString(level)). + SetString(level.String()). Bold(true). - MaxWidth(4). //nolint:mnd // 4 is the max width for the level string + MaxWidth(maxWidth). Foreground(lipgloss.Color(color)) } @@ -156,8 +157,8 @@ func newCustomStyles() *clog.Styles { // replaceAttr is the replacement function for slog.HandlerOptions. func replaceAttr(_ []string, a slog.Attr) slog.Attr { if a.Key == slog.LevelKey { - lev := a.Value.Any().(Level) - a.Value = slog.StringValue(getLevelString(lev)) + lev := a.Value.Any().(slog.Level) + a.Value = slog.StringValue(lev.String()) } return a } diff --git a/internal/logger/utils_test.go b/internal/logger/utils_test.go index 13bbb85..da53441 100644 --- a/internal/logger/utils_test.go +++ b/internal/logger/utils_test.go @@ -89,8 +89,8 @@ func TestNewLogger(t *testing.T) { } if tt.levelEnv != "" { - want := getLevel(tt.levelEnv) - got := log.Enabled(context.Background(), slog.Level(want)) + want := newLevel(tt.levelEnv) + got := log.Enabled(context.Background(), want) if !got { t.Errorf("Expected log level: %v", want) } diff --git a/logger/logger.go b/logger/logger.go index 78d4927..3549a76 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -10,8 +10,9 @@ import ( // Logger is an alias for the [Provider] interface. // It is defined for backward compatibility with previous versions of the logger package. -// FIXME: Will be removed with v0.4.0. -type Logger = Provider +// +// Deprecated: Use [Provider] instead. +type Logger = Provider // TODO: Remove in v0.4.0 // Provider is the interface for the logger. // Its build on top of slog.Logger and extends it with additional logging methods. @@ -20,6 +21,28 @@ type Provider = logger.Provider // Options is the optional configuration for the logger. type Options = logger.Options +// Level is a custom type for log levels. +type Level = logger.Level + +const ( + // LevelTrace represents the TRACE log level. + LevelTrace = logger.LevelTrace + // LevelDebug represents the DEBUG log level. + LevelDebug = logger.LevelDebug + // LevelInfo represents the INFO log level. + LevelInfo = logger.LevelInfo + // LevelNotice represents the NOTICE log level. + LevelNotice = logger.LevelNotice + // LevelWarn represents the WARN log level. + LevelWarn = logger.LevelWarn + // LevelError represents the ERROR log level. + LevelError = logger.LevelError + // LevelPanic represents the PANIC log level. + LevelPanic = logger.LevelPanic + // LevelFatal represents the FATAL log level. + LevelFatal = logger.LevelFatal +) + // NewLogger creates a new Logger instance with optional configurations. // The logger can be customized by passing an Options struct which allows for // setting the log level, format, OpenTelemetry support, and a custom handler.