spacemeshos / go-spacemesh

Go Implementation of the Spacemesh protocol full node. 💾⏰💪
https://spacemesh.io
MIT License
752 stars 211 forks source link

Migrate to Go 1.21 slog package for logging. #4786

Open fasmat opened 1 year ago

fasmat commented 1 year ago

Description

With Go 1.21 a new structured logger log/slog will make its way into the standard library. It seems to be as powerful and efficient as zap and would allow us to drop one dependency.

Steps necessary for migration:

dshulyak commented 1 year ago

but is there actually an advantage in using slog over zap? i am not against it, but i am heavily using zap Marshal interface, and migrating out of it without a clear advantage is questionable for me

fasmat commented 1 year ago

The primary advantage is to not depend on an external library if similar functionality is provided by the standard library. The performance of both loggers should be similar.

The equivalent to the zapcore.ObjectMarshaller interface in slog seems to be https://pkg.go.dev/log/slog#LogValuer that can also be used to lazily serialize an object to text for the logger (and won't be called if the log won't be printed). There is also a section going into performance considerations for logging: https://pkg.go.dev/log/slog#hdr-Performance_considerations

I haven't played around with slog enough yet to know if there are more advantages or disadvantages to using it over zap.

dshulyak commented 1 year ago

another thing that i would really like to have is to increase/decrease level with API without restarting a node. zap already provides a way to do it, it is just not implemented because our logging is a bit of a mess. we should ensure that it can be done in slog too

fasmat commented 1 year ago

Changing the logging level of an slog.Logger can be done during runtime:

var programLevel = new(slog.LevelVar) // Info by default

h := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: programLevel})
logger := slog.New(h)

// logger only logs Info or higher

programLevel.Set(slog.LevelDebug)

// now logger logs debug as well

To make this possible on a per-service basis we probably need multiple handlers, one for each component:

var hareLevel = new(slog.LevelVar)
hareLevel.Set(slog.LevelDebug)
hareLogger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: hareLevel}))

var atxHandlerLevel = new(slog.LevelVar)
atxHandlerLevel.Set(slog.LevelError)
atxHandlerLogger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: atxHandlerLevel}))

see also: https://pkg.go.dev/log/slog@go1.21rc4#hdr-Levels

I'm not 100% sure if multiple handlers using the same io.Writer is safe to use, but it appears to be. At least https://github.com/golang/example/blob/master/slog-handler-guide/README.md#the-handle-method and https://pkg.go.dev/log/slog#JSONHandler.Handle mention that "each call to slog.JSONHandler.Handle results in a single serialized call to io.Writer.Write" "to minimize interleaving with other goroutines using the same writer."