From e2030146d59b6f4706991e07fda96b0508c7c74d Mon Sep 17 00:00:00 2001 From: Patryk Osmaczko Date: Mon, 29 Sep 2025 23:45:49 +0200 Subject: [PATCH] chore: adapt to new geth logger --- logutils/geth_adapter.go | 103 +++++++++++++++++++++++----------- logutils/geth_adapter_test.go | 6 +- logutils/logger.go | 3 +- logutils/lvl_from_string.go | 28 +++++++++ 4 files changed, 102 insertions(+), 38 deletions(-) create mode 100644 logutils/lvl_from_string.go diff --git a/logutils/geth_adapter.go b/logutils/geth_adapter.go index db6c55c3c61..ac5b65b3380 100644 --- a/logutils/geth_adapter.go +++ b/logutils/geth_adapter.go @@ -1,7 +1,8 @@ package logutils import ( - "fmt" + "context" + "log/slog" "strings" "go.uber.org/zap" @@ -10,42 +11,78 @@ import ( "github.com/ethereum/go-ethereum/log" ) -// gethAdapter returns a log.Handler interface that forwards logs to a zap.Logger. -// Logs are forwarded raw as if geth were printing them. -func gethAdapter(logger *zap.Logger) log.Handler { - return log.FuncHandler(func(r *log.Record) error { - level, err := LvlFromString(r.Lvl.String()) - if err != nil { - return err - } - // Skip trace logs to not clutter the output - if level == traceLevel { - return nil - } - serializedLog := string(log.TerminalFormat(false).Format(r)) - logger.Check(level, fmt.Sprintf("'%s'", strings.TrimSuffix(serializedLog, "\n"))).Write() - return nil - }) +type gethAdapter struct { + log.Logger + target *zap.Logger +} + +func newGethAdapter(target *zap.Logger) *gethAdapter { + return &gethAdapter{ + Logger: log.NewLogger(log.DiscardHandler()), + target: target, + } +} + +func (g *gethAdapter) serializeLog(level slog.Level, msg string, ctx ...interface{}) string { + buf := strings.Builder{} + logger := log.NewLogger(log.LogfmtHandlerWithLevel(&buf, level)) + logger.Log(level, msg, ctx...) + return buf.String() +} + +func (g *gethAdapter) Log(level slog.Level, msg string, ctx ...interface{}) { + g.Write(level, msg, ctx...) +} + +func (g *gethAdapter) Trace(msg string, ctx ...interface{}) { + g.Write(log.LevelTrace, msg, ctx...) +} + +func (g *gethAdapter) Debug(msg string, ctx ...interface{}) { + g.Write(log.LevelDebug, msg, ctx...) +} + +func (g *gethAdapter) Info(msg string, ctx ...interface{}) { + g.Write(log.LevelInfo, msg, ctx...) +} + +func (g *gethAdapter) Warn(msg string, ctx ...interface{}) { + g.Write(log.LevelWarn, msg, ctx...) +} + +func (g *gethAdapter) Error(msg string, ctx ...interface{}) { + g.Write(log.LevelError, msg, ctx...) +} + +func (g *gethAdapter) Crit(msg string, ctx ...interface{}) { + g.Write(log.LevelCrit, msg, ctx...) +} + +func (g *gethAdapter) Write(level slog.Level, msg string, attrs ...any) { + g.target.Check(zapLevelFromGeth(level), g.serializeLog(level, msg, attrs...)).Write() +} + +func (g *gethAdapter) Enabled(ctx context.Context, level slog.Level) bool { + return g.target.Core().Enabled(zapLevelFromGeth(level)) && g.Logger.Enabled(ctx, level) } const traceLevel = zapcore.DebugLevel - 1 -// LvlFromString returns the appropriate zapcore.Level from a string. -func LvlFromString(lvlString string) (zapcore.Level, error) { - switch strings.ToLower(lvlString) { - case "trace", "trce": - return traceLevel, nil // zap does not have a trace level, use custom - case "debug", "dbug": - return zapcore.DebugLevel, nil - case "info": - return zapcore.InfoLevel, nil - case "warn": - return zapcore.WarnLevel, nil - case "error", "eror": - return zapcore.ErrorLevel, nil - case "crit": - return zapcore.DPanicLevel, nil // zap does not have a crit level, using DPanicLevel as closest +func zapLevelFromGeth(lvl slog.Level) zapcore.Level { + switch lvl { + case log.LevelTrace: + return traceLevel // zap does not have a trace level, use custom + case log.LevelDebug: + return zapcore.DebugLevel + case log.LevelInfo: + return zapcore.InfoLevel + case log.LevelWarn: + return zapcore.WarnLevel + case log.LevelError: + return zapcore.ErrorLevel + case log.LevelCrit: + return zapcore.DPanicLevel // zap does not have a crit level, using DPanicLevel as closest default: - return zapcore.InvalidLevel, fmt.Errorf("unknown level: %v", lvlString) + return zapcore.InvalidLevel } } diff --git a/logutils/geth_adapter_test.go b/logutils/geth_adapter_test.go index af9b5f5353d..ca944918ef2 100644 --- a/logutils/geth_adapter_test.go +++ b/logutils/geth_adapter_test.go @@ -23,19 +23,19 @@ func TestGethAdapter(t *testing.T) { ) logger := zap.New(core) - log.Root().SetHandler(gethAdapter(logger)) + log.SetDefault(newGethAdapter(logger)) log.Debug("should not be printed, as it's below the log level") require.Empty(t, buffer.String()) buffer.Reset() log.Info("should be printed") - require.Regexp(t, `INFO\s+'INFO\s*\[.*\]\s*should be printed '`, buffer.String()) + require.Regexp(t, `INFO\s+t=.* lvl=info msg="should be printed"`, buffer.String()) buffer.Reset() level.SetLevel(zap.DebugLevel) log.Debug("should be printed with context", "value1", 12345, "value2", "string") - require.Regexp(t, `DEBUG\s+'DEBUG\s*\[.*\]\s*should be printed with context\s+value1=12345\s+value2=string'`, buffer.String()) + require.Regexp(t, `DEBUG\s+t=.* lvl=debug msg="should be printed with context" value1=12345 value2=string`, buffer.String()) buffer.Reset() log.Trace("should be skipped") diff --git a/logutils/logger.go b/logutils/logger.go index f7e828f788b..85cb6cdcd5d 100644 --- a/logutils/logger.go +++ b/logutils/logger.go @@ -22,8 +22,7 @@ func ZapLogger() *zap.Logger { _zapLogger = defaultLogger() // forward geth logs to zap logger - _gethLogger := _zapLogger.Named("geth") - log.Root().SetHandler(gethAdapter(_gethLogger)) + log.SetDefault(newGethAdapter(_zapLogger.Named("geth"))) }) return _zapLogger } diff --git a/logutils/lvl_from_string.go b/logutils/lvl_from_string.go new file mode 100644 index 00000000000..ccf58ccb2b9 --- /dev/null +++ b/logutils/lvl_from_string.go @@ -0,0 +1,28 @@ +package logutils + +import ( + "fmt" + "strings" + + "go.uber.org/zap/zapcore" +) + +// LvlFromString returns the appropriate zapcore.Level from a string. +func LvlFromString(lvlString string) (zapcore.Level, error) { + switch strings.ToLower(lvlString) { + case "trace", "trce": + return traceLevel, nil // zap does not have a trace level, use custom + case "debug", "dbug": + return zapcore.DebugLevel, nil + case "info": + return zapcore.InfoLevel, nil + case "warn": + return zapcore.WarnLevel, nil + case "error", "eror": + return zapcore.ErrorLevel, nil + case "crit": + return zapcore.DPanicLevel, nil // zap does not have a crit level, using DPanicLevel as closest + default: + return zapcore.InvalidLevel, fmt.Errorf("unknown level: %v", lvlString) + } +}