2 min read
The default handler
Much like the older log package, log/slog ships with a “default logger”. You can use this logger without doing any configuration, just by calling any of the package-level logging functions: slog.Error("oh noes!") While you’d probably never want to use the default logger in a serious server application, it can be a convenience for small or throw-away utilities. But how does it work? That’s today’s topic! Overview … The default handler formats the log record’s message, time, level, and attributes as a string and passes it to the log package.
2 min read
The default handler
Much like the older log package, log/slog ships with a “default logger”. You can use this logger without doing any configuration, just by calling any of the package-level logging functions: slog.Error("oh noes!") While you’d probably never want to use the default logger in a serious server application, it can be a convenience for small or throw-away utilities. But how does it work? That’s today’s topic! Overview … The default handler formats the log record’s message, time, level, and attributes as a string and passes it to the log package.
2 min read
Intro to slog levels
By default, the log/slog package supports four log levels: Debug, Info, Warn, and Error, each of which has matching logger methods: Overview … The Info top-level function calls the Logger.Info method on the default Logger. In addition to Logger.Info, there are methods for Debug, Warn and Error levels. Besides these convenience methods for common levels, there is also a Logger.Log method which takes the level as an argument. Each of these methods has a corresponding top-level function that uses the default logger.
1 min read
slog Records and levels
Yesterday we saw that slog uses handlers to process/record logs. But what exactly does it process? Records! Overview … A log record consists of a time, a level, a message, and a set of key-value pairs, where the keys are strings and the values may be of any type. As an example, slog.Info("hello", "count", 3) creates a record containing the time of the call, a level of Info, the message “hello”, and a single pair with key “count” and value 3.
2 min read
Anatomy of a log/slog logger
Unlike the older log package, which provides a single *log.Logger type as its primary interface, log/slog has a two-tiered architecture. This is roughly the same architecture used by the database/sql package: One interface implements a handler (or “driver” for database/sql), and another interface is consumed. The package itself provides the intermediate translation. This is essentially a localized example of ports-and-adaptors or hexagonal architecture. Here’s how the GoDoc for the package explains it:
2 min read
Let's talk about logging
I’ve been absent far too long. Most days I think about writing something again, then… I don’t. 🤷♂️ I’m going to try to get back into the habit… and this time, I thought I’d talk about one of my favorite packages in the standard library: log/slog. To kick off, I’ll give a general description of the package, then starting tomorrow (I promise! I won’t forget again!) we’ll start into the particulars.
2 min read
When not to use context values
Storing values in a context is quite flexible, but comes without strict type safety, and obscures the API. So what can we do about this? First, for type safety, we’re limited to runtime assertions: userID := ctx.Value(userIDKey).(string) But this can panic if we ever get an unexpected value (or even nil). So to make it safer, we can use the two-variable assignment form, which yields a bool indicating success: userID, ok := ctx.