b8d44ed98b
This change - Removes interface `log.Format`, - Removes method `log.FormatFunc`, - unexports `TerminalHandler.TerminalFormat` formatting methods (renamed to `TerminalHandler.format`) - removes the notion of `log.Lazy` values The lazy handler was useful in the old log package, since it could defer the evaluation of costly attributes until later in the log pipeline: thus, if the logging was done at 'Trace', we could skip evaluation if logging only was set to 'Info'. With the move to slog, this way of deferring evaluation is no longer needed, since slog introduced 'Enabled': the caller can thus do the evaluate-or-not decision at the callsite, which is much more straight-forward than dealing with lazy reflect-based evaluation. Also, lazy evaluation would not work with 'native' slog, as in, these two statements would be evaluated differently: ```golang log.Info("foo", "my lazy", lazyObj) slog.Info("foo", "my lazy", lazyObj) ```
211 lines
5.1 KiB
Go
211 lines
5.1 KiB
Go
package log
|
|
|
|
import (
|
|
"context"
|
|
"math"
|
|
"os"
|
|
"runtime"
|
|
"time"
|
|
|
|
"golang.org/x/exp/slog"
|
|
)
|
|
|
|
const errorKey = "LOG_ERROR"
|
|
|
|
const (
|
|
legacyLevelCrit = iota
|
|
legacyLevelError
|
|
legacyLevelWarn
|
|
legacyLevelInfo
|
|
legacyLevelDebug
|
|
legacyLevelTrace
|
|
)
|
|
|
|
const (
|
|
levelMaxVerbosity slog.Level = math.MinInt
|
|
LevelTrace slog.Level = -8
|
|
LevelDebug = slog.LevelDebug
|
|
LevelInfo = slog.LevelInfo
|
|
LevelWarn = slog.LevelWarn
|
|
LevelError = slog.LevelError
|
|
LevelCrit slog.Level = 12
|
|
|
|
// for backward-compatibility
|
|
LvlTrace = LevelTrace
|
|
LvlInfo = LevelInfo
|
|
LvlDebug = LevelDebug
|
|
)
|
|
|
|
// convert from old Geth verbosity level constants
|
|
// to levels defined by slog
|
|
func FromLegacyLevel(lvl int) slog.Level {
|
|
switch lvl {
|
|
case legacyLevelCrit:
|
|
return LevelCrit
|
|
case legacyLevelError:
|
|
return slog.LevelError
|
|
case legacyLevelWarn:
|
|
return slog.LevelWarn
|
|
case legacyLevelInfo:
|
|
return slog.LevelInfo
|
|
case legacyLevelDebug:
|
|
return slog.LevelDebug
|
|
case legacyLevelTrace:
|
|
return LevelTrace
|
|
default:
|
|
break
|
|
}
|
|
|
|
// TODO: should we allow use of custom levels or force them to match existing max/min if they fall outside the range as I am doing here?
|
|
if lvl > legacyLevelTrace {
|
|
return LevelTrace
|
|
}
|
|
return LevelCrit
|
|
}
|
|
|
|
// LevelAlignedString returns a 5-character string containing the name of a Lvl.
|
|
func LevelAlignedString(l slog.Level) string {
|
|
switch l {
|
|
case LevelTrace:
|
|
return "TRACE"
|
|
case slog.LevelDebug:
|
|
return "DEBUG"
|
|
case slog.LevelInfo:
|
|
return "INFO "
|
|
case slog.LevelWarn:
|
|
return "WARN "
|
|
case slog.LevelError:
|
|
return "ERROR"
|
|
case LevelCrit:
|
|
return "CRIT "
|
|
default:
|
|
return "unknown level"
|
|
}
|
|
}
|
|
|
|
// LevelString returns a 5-character string containing the name of a Lvl.
|
|
func LevelString(l slog.Level) string {
|
|
switch l {
|
|
case LevelTrace:
|
|
return "trace"
|
|
case slog.LevelDebug:
|
|
return "debug"
|
|
case slog.LevelInfo:
|
|
return "info"
|
|
case slog.LevelWarn:
|
|
return "warn"
|
|
case slog.LevelError:
|
|
return "eror"
|
|
case LevelCrit:
|
|
return "crit"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// A Logger writes key/value pairs to a Handler
|
|
type Logger interface {
|
|
// With returns a new Logger that has this logger's attributes plus the given attributes
|
|
With(ctx ...interface{}) Logger
|
|
|
|
// With returns a new Logger that has this logger's attributes plus the given attributes. Identical to 'With'.
|
|
New(ctx ...interface{}) Logger
|
|
|
|
// Log logs a message at the specified level with context key/value pairs
|
|
Log(level slog.Level, msg string, ctx ...interface{})
|
|
|
|
// Trace log a message at the trace level with context key/value pairs
|
|
Trace(msg string, ctx ...interface{})
|
|
|
|
// Debug logs a message at the debug level with context key/value pairs
|
|
Debug(msg string, ctx ...interface{})
|
|
|
|
// Info logs a message at the info level with context key/value pairs
|
|
Info(msg string, ctx ...interface{})
|
|
|
|
// Warn logs a message at the warn level with context key/value pairs
|
|
Warn(msg string, ctx ...interface{})
|
|
|
|
// Error logs a message at the error level with context key/value pairs
|
|
Error(msg string, ctx ...interface{})
|
|
|
|
// Crit logs a message at the crit level with context key/value pairs, and exits
|
|
Crit(msg string, ctx ...interface{})
|
|
|
|
// Write logs a message at the specified level
|
|
Write(level slog.Level, msg string, attrs ...any)
|
|
|
|
// Enabled reports whether l emits log records at the given context and level.
|
|
Enabled(ctx context.Context, level slog.Level) bool
|
|
}
|
|
|
|
type logger struct {
|
|
inner *slog.Logger
|
|
}
|
|
|
|
// NewLogger returns a logger with the specified handler set
|
|
func NewLogger(h slog.Handler) Logger {
|
|
return &logger{
|
|
slog.New(h),
|
|
}
|
|
}
|
|
|
|
// write logs a message at the specified level:
|
|
func (l *logger) Write(level slog.Level, msg string, attrs ...any) {
|
|
if !l.inner.Enabled(context.Background(), level) {
|
|
return
|
|
}
|
|
|
|
var pcs [1]uintptr
|
|
runtime.Callers(3, pcs[:])
|
|
|
|
if len(attrs)%2 != 0 {
|
|
attrs = append(attrs, nil, errorKey, "Normalized odd number of arguments by adding nil")
|
|
}
|
|
r := slog.NewRecord(time.Now(), level, msg, pcs[0])
|
|
r.Add(attrs...)
|
|
l.inner.Handler().Handle(context.Background(), r)
|
|
}
|
|
|
|
func (l *logger) Log(level slog.Level, msg string, attrs ...any) {
|
|
l.Write(level, msg, attrs...)
|
|
}
|
|
|
|
func (l *logger) With(ctx ...interface{}) Logger {
|
|
return &logger{l.inner.With(ctx...)}
|
|
}
|
|
|
|
func (l *logger) New(ctx ...interface{}) Logger {
|
|
return l.With(ctx...)
|
|
}
|
|
|
|
// Enabled reports whether l emits log records at the given context and level.
|
|
func (l *logger) Enabled(ctx context.Context, level slog.Level) bool {
|
|
return l.inner.Enabled(ctx, level)
|
|
}
|
|
|
|
func (l *logger) Trace(msg string, ctx ...interface{}) {
|
|
l.Write(LevelTrace, msg, ctx...)
|
|
}
|
|
|
|
func (l *logger) Debug(msg string, ctx ...interface{}) {
|
|
l.Write(slog.LevelDebug, msg, ctx...)
|
|
}
|
|
|
|
func (l *logger) Info(msg string, ctx ...interface{}) {
|
|
l.Write(slog.LevelInfo, msg, ctx...)
|
|
}
|
|
|
|
func (l *logger) Warn(msg string, ctx ...any) {
|
|
l.Write(slog.LevelWarn, msg, ctx...)
|
|
}
|
|
|
|
func (l *logger) Error(msg string, ctx ...interface{}) {
|
|
l.Write(slog.LevelError, msg, ctx...)
|
|
}
|
|
|
|
func (l *logger) Crit(msg string, ctx ...interface{}) {
|
|
l.Write(LevelCrit, msg, ctx...)
|
|
os.Exit(1)
|
|
}
|