diff --git a/internal/testlog/testlog.go b/internal/testlog/testlog.go index e2c6079d18..fdf537a5bc 100644 --- a/internal/testlog/testlog.go +++ b/internal/testlog/testlog.go @@ -102,6 +102,8 @@ func (l *logger) Inner() *slog.Logger { return l.l.Inner() } +func (l *logger) Write(level slog.Level, msg string, ctx ...interface{}) {} + func (l *logger) Trace(msg string, ctx ...interface{}) { l.t.Helper() l.mu.Lock() diff --git a/log/handler.go b/log/handler.go index 4068a09a5f..8d43a44736 100644 --- a/log/handler.go +++ b/log/handler.go @@ -212,6 +212,8 @@ func builtinReplace(_ []string, attr slog.Attr, logfmt bool) slog.Attr { } else { attr.Value = slog.StringValue(v.Dec()) } + case fmt.Stringer: + attr.Value = slog.StringValue(v.String()) } return attr } diff --git a/log/logger.go b/log/logger.go index 2af04959ab..903b329f1f 100644 --- a/log/logger.go +++ b/log/logger.go @@ -136,6 +136,9 @@ type Logger interface { // Inner returns the underlying slog logger that is wrapped Inner() *slog.Logger + + // Write logs a message at the specified level + Write(level slog.Level, msg string, attrs ...any) } type logger struct { @@ -155,7 +158,7 @@ func (l *logger) Inner() *slog.Logger { } // write logs a message at the specified level: -func (l *logger) write(level slog.Level, msg string, attrs ...any) { +func (l *logger) Write(level slog.Level, msg string, attrs ...any) { if !l.inner.Enabled(context.Background(), level) { return } @@ -192,7 +195,7 @@ func (l *logger) write(level slog.Level, msg string, attrs ...any) { } func (l *logger) Log(level slog.Level, msg string, attrs ...any) { - l.write(level, msg, attrs...) + l.Write(level, msg, attrs...) } func (l *logger) With(ctx ...interface{}) Logger { @@ -204,26 +207,26 @@ func (l *logger) New(ctx ...interface{}) Logger { } func (l *logger) Trace(msg string, ctx ...interface{}) { - l.write(LevelTrace, msg, ctx...) + l.Write(LevelTrace, msg, ctx...) } func (l *logger) Debug(msg string, ctx ...interface{}) { - l.write(slog.LevelDebug, msg, ctx...) + l.Write(slog.LevelDebug, msg, ctx...) } func (l *logger) Info(msg string, ctx ...interface{}) { - l.write(slog.LevelInfo, msg, ctx...) + l.Write(slog.LevelInfo, msg, ctx...) } func (l *logger) Warn(msg string, ctx ...any) { - l.write(slog.LevelWarn, msg, ctx...) + l.Write(slog.LevelWarn, msg, ctx...) } func (l *logger) Error(msg string, ctx ...interface{}) { - l.write(slog.LevelError, msg, ctx...) + l.Write(slog.LevelError, msg, ctx...) } func (l *logger) Crit(msg string, ctx ...interface{}) { - l.write(LevelCrit, msg, ctx...) + l.Write(LevelCrit, msg, ctx...) os.Exit(1) } diff --git a/log/root.go b/log/root.go index fcd2cf38f5..bdc37c9e3b 100644 --- a/log/root.go +++ b/log/root.go @@ -2,42 +2,30 @@ package log import ( "os" - "sync" "golang.org/x/exp/slog" ) -var ( - rootMu sync.Mutex - root logger -) +var root Logger func init() { defaultLogger := &logger{slog.New(DiscardHandler())} SetDefault(defaultLogger) } +// SetDefault sets the default global logger func SetDefault(l Logger) { - rootMu.Lock() - defer rootMu.Unlock() - root := l + root = l slog.SetDefault(root.Inner()) } // Root returns the root logger func Root() Logger { - return rootLogger() -} - -func rootLogger() *logger { - rootMu.Lock() - defer rootMu.Unlock() - res := root - return &res + return Root() } // The following functions bypass the exported logger methods (logger.Debug, -// etc.) to keep the call depth the same for all paths to logger.write so +// etc.) to keep the call depth the same for all paths to logger.Write so // runtime.Caller(2) always refers to the call site in client code. // Trace is a convenient alias for Root().Trace @@ -50,7 +38,7 @@ func rootLogger() *logger { // log.Trace("msg", "key1", val1) // log.Trace("msg", "key1", val1, "key2", val2) func Trace(msg string, ctx ...interface{}) { - rootLogger().write(LevelTrace, msg, ctx...) + Root().Write(LevelTrace, msg, ctx...) } // Debug is a convenient alias for Root().Debug @@ -63,7 +51,7 @@ func Trace(msg string, ctx ...interface{}) { // log.Debug("msg", "key1", val1) // log.Debug("msg", "key1", val1, "key2", val2) func Debug(msg string, ctx ...interface{}) { - rootLogger().write(slog.LevelDebug, msg, ctx...) + Root().Write(slog.LevelDebug, msg, ctx...) } // Info is a convenient alias for Root().Info @@ -76,7 +64,7 @@ func Debug(msg string, ctx ...interface{}) { // log.Info("msg", "key1", val1) // log.Info("msg", "key1", val1, "key2", val2) func Info(msg string, ctx ...interface{}) { - rootLogger().write(slog.LevelInfo, msg, ctx...) + Root().Write(slog.LevelInfo, msg, ctx...) } // Warn is a convenient alias for Root().Warn @@ -89,7 +77,7 @@ func Info(msg string, ctx ...interface{}) { // log.Warn("msg", "key1", val1) // log.Warn("msg", "key1", val1, "key2", val2) func Warn(msg string, ctx ...interface{}) { - rootLogger().write(slog.LevelWarn, msg, ctx...) + Root().Write(slog.LevelWarn, msg, ctx...) } // Error is a convenient alias for Root().Error @@ -102,7 +90,7 @@ func Warn(msg string, ctx ...interface{}) { // log.Error("msg", "key1", val1) // log.Error("msg", "key1", val1, "key2", val2) func Error(msg string, ctx ...interface{}) { - rootLogger().write(slog.LevelError, msg, ctx...) + Root().Write(slog.LevelError, msg, ctx...) } // Crit is a convenient alias for Root().Crit @@ -115,12 +103,12 @@ func Error(msg string, ctx ...interface{}) { // log.Crit("msg", "key1", val1) // log.Crit("msg", "key1", val1, "key2", val2) func Crit(msg string, ctx ...interface{}) { - rootLogger().write(LevelCrit, msg, ctx...) + Root().Write(LevelCrit, msg, ctx...) os.Exit(1) } // New returns a new logger with the given context. // New is a convenient alias for Root().New func New(ctx ...interface{}) Logger { - return rootLogger().With(ctx...) + return Root().With(ctx...) }