Skip to content

Commit

Permalink
refactor: implement level string function as method
Browse files Browse the repository at this point in the history
Signed-off-by: lvlcn-t <[email protected]>
  • Loading branch information
lvlcn-t committed Nov 15, 2024
1 parent 9e9ea51 commit a349126
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 55 deletions.
4 changes: 2 additions & 2 deletions examples/extension/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
}
}
Expand Down
17 changes: 11 additions & 6 deletions internal/logger/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -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].
Expand Down Expand Up @@ -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.
Expand All @@ -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()
Expand Down
20 changes: 10 additions & 10 deletions internal/logger/extensions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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())
Expand Down
46 changes: 23 additions & 23 deletions internal/logger/level.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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"
Expand Down
4 changes: 2 additions & 2 deletions internal/logger/level_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
})
Expand Down
17 changes: 9 additions & 8 deletions internal/logger/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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()
Expand Down Expand Up @@ -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,
})
Expand All @@ -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,
})
}
Expand All @@ -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))
}

Expand All @@ -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
}
4 changes: 2 additions & 2 deletions internal/logger/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
27 changes: 25 additions & 2 deletions logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down

0 comments on commit a349126

Please sign in to comment.