28e7371701
This PR replaces Geth's logger package (a fork of [log15](https://github.com/inconshreveable/log15)) with an implementation using slog, a logging library included as part of the Go standard library as of Go1.21. Main changes are as follows: * removes any log handlers that were unused in the Geth codebase. * Json, logfmt, and terminal formatters are now slog handlers. * Verbosity level constants are changed to match slog constant values. Internal translation is done to make this opaque to the user and backwards compatible with existing `--verbosity` and `--vmodule` options. * `--log.backtraceat` and `--log.debug` are removed. The external-facing API is largely the same as the existing Geth logger. Logger method signatures remain unchanged. A small semantic difference is that a `Handler` can only be set once per `Logger` and not changed dynamically. This just means that a new logger must be instantiated every time the handler of the root logger is changed. ---- For users of the `go-ethereum/log` module. If you were using this module for your own project, you will need to change the initialization. If you previously did ```golang log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) ``` You now instead need to do ```golang log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true))) ``` See more about reasoning here: https://github.com/ethereum/go-ethereum/issues/28558#issuecomment-1820606613
223 lines
5.1 KiB
Go
223 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)
|
|
}
|
|
|
|
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")
|
|
}
|
|
|
|
// evaluate lazy values
|
|
var hadErr bool
|
|
for i := 1; i < len(attrs); i += 2 {
|
|
lz, ok := attrs[i].(Lazy)
|
|
if ok {
|
|
v, err := evaluateLazy(lz)
|
|
if err != nil {
|
|
hadErr = true
|
|
attrs[i] = err
|
|
} else {
|
|
attrs[i] = v
|
|
}
|
|
}
|
|
}
|
|
|
|
if hadErr {
|
|
attrs = append(attrs, errorKey, "bad lazy")
|
|
}
|
|
|
|
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...)
|
|
}
|
|
|
|
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)
|
|
}
|