# Migrating Prysm to Slog ```go! logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) slog.SetDefault(logger) slog.Info("message") slog.Error("message") slog.Debug("message") slog.Warn("message") ``` Contextual logging: Instead of `logrus.WithFields`, contextual logs can be added in a key=>value style. A problem is it not type-safe and can mess up with unbalanced keys. ```go! logger.Info( "incoming request", "method", "GET", "time_taken_ms", 158, "path", "/hello/world?q=search", "status", 200, "user_agent", "Googlebot/2.1 (+http://www.google.com/bot.html)", ) ``` Compared to logrus, however, we get strongly-typed fields with this usage: ```go! logger.Info( "incoming request", slog.String("method", "GET"), slog.Int("time_taken_ms", 158), slog.String("path", "/hello/world?q=search"), slog.Int("status", 200), slog.String( "user_agent", "Googlebot/2.1 (+http://www.google.com/bot.html)", ), ) ``` We can also create grouped attributes: ```go! logger.LogAttrs( context.Background(), slog.LevelInfo, "image uploaded", slog.Int("id", 23123), slog.Group("properties", slog.Int("width", 4000), slog.Int("height", 3000), slog.String("format", "jpeg"), ), ) ``` Child loggers can be helpful as part of a particular function, as groups can be attached to them. For example, we could have a group logger in trace mode or custom mode for different properties such as peers, attestation contents, etc. ```go= child := logger.With( slog.Group("program_info", slog.Int("pid", os.Getpid()), slog.String("go_version", buildInfo.GoVersion), ), ) ``` We can customize log levels, which would be excellent for p2p and for user experience: ```go= var LevelNames = map[slog.Leveler]string{ LevelTrace: "TRACE", LevelNotice: "NOTICE", LevelFatal: "FATAL", } ``` The source of a log can also be included in the log itself: ```go= opts := slog.HandlerOptions{ AddSource: true, Level: slog.LevelDebug, } "source":{"function":"main.main","file":"/home/ayo/dev/demo/slog/main.go","line":30} ``` We can also implement a `LogValue` interface on any of our types, including attestations, blocks, or any struct to customize how they will be logged, which is very helpful to minimize use of contextual logging. ```go func (u *User) LogValue() slog.Value { return slog.GroupValue( slog.String("id", u.ID), slog.String("name", u.FirstName+" "+u.LastName), ) } ``` Handlers can be customized for nice colors and formatting in any way we prefer. We could offer users a nicer logging experience this way: ```go func main() { opts := PrettyHandlerOptions{ SlogOpts: slog.HandlerOptions{ Level: slog.LevelDebug, }, } handler := NewPrettyHandler(os.Stdout, opts) logger := slog.New(handler) slog.SetDefault(logger) slog.Debug( "executing database query", slog.String("query", "SELECT * FROM users"), ) slog.Info("image upload successful", slog.String("image_id", "39ud88")) slog.Warn( "storage is 90% full", slog.String("available_space", "900.1 MB"), ) slog.Error( "An error occurred while processing the request", slog.String("url", "https://example.com"), ) } ``` With output: ![](https://hackmd.io/_uploads/HJrWLZYA3.png) # Integration Here are some things to note about integrating these changes - [ ] Fluentd integration and correct JSON formatting (need to make sure that the slog prysm format with JSON matches the fluentd format) - [ ] File output works correctly: need to make sure that outputting our Prysm slogs to a file work the same as with logrus - [ ] Determine which types could implement the `LogValue` interface in Prysm to make them easy to log and avoid a major refactoring - [ ] Ensure prefixes work - [ ] Keep debug / info / warn / error the same in the first refactor, then introduce a custom p2p log level and change some of our logs to that - [ ] Start with the validator client: change all logrus logs in there - [ ] The webUI supports log streaming: ensure we can still do this