Let's talk about logging

March 31, 2026

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.

First there was log

The log package has been with us since the very beginning of Go. But few use it for serious applications, because it’s so limited. No real log levels–just “info” (or info + panic, or info + os.Exit), plain text output (no JSON or other formats), and thus no meaningful middleware capabilities.

These limitations lead to the proliferation of many third-party logging libraries such as Logrus, Zap, and ZeroLog.

Introducing log/slog

Finally, in June, 2023, with Go 1.21, log/slog was introduced. And now, except for very special cases, it should probably be your go-to logger. It offers:

  • LevelsDebug, Info, Warn, and Error by default, but it’s customizable, so you can also have Super Duper Important I really Mean it, I do! as a level, if you want!
  • Structured output — Key/value pairs for the win!
  • Pluggable handlers — Want JSON output? Handled. Standard text more to your liking? You got it. Want color on the console? No problem.
  • Composable handlers — Want JSON sent to your logging service, plain text to your log file, and color to your console, all at the same time? We have that, too!
  • Middleware — Want to filter passwords out of your logs? Or add special annotations to every error? Or replace every occurrence of the word “hippopotamus” with “rhinoceros” for unexplicable reasons? You can.

If you’ve never used log/slog, this series will be for you.

If you already use log/slog, you’ll probably learn something new anyway!

So let’s go!


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


slog.DiscardHandler

Now that we’ve made it through the Handler interface, let’s look at the first of the implementations provided by the standard library: the DiscardHandler. var DiscardHandler Handler = discardHandler{} DiscardHandler discards all log output. DiscardHandler.Enabled returns false for all Levels. Why would you ever want a discard handler? Two likely scenarios come to mind. The one where I most frequently use it, is testing. You may not wish to log anything during a test, and this is a simple way to do that:


slog.Handler

I’m probably doing this in backwards order, but that’s the order the GoDoc presents it… Now that we’ve looked at each of the methods of the Handler interface, we come to its general description: type Handler A Handler handles log records produced by a Logger. A typical handler may print log records to standard error, or write them to a file or database, or perhaps augment them with additional attributes and pass them on to another handler.


slog.Handler.WithGroup

I hope everyone had a good Labor Day weekend… even if you’re not in the US and had to/got to labor on Monday. Speaking of labor, I’m looking for new clients! If you could use some world class Go expertise on your project, please reach out! type Handler type Handler interface { … // WithGroup returns a new Handler with the given group appended to // the receiver's existing groups. // The keys of all subsequent attributes, whether added by With or in a // Record, should be qualified by the sequence of group names.

Get daily content like this in your inbox!

Subscribe