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


Log levels

I'm launching a new live course: **Idiomatic Testing in Go**. The course begins May 5. Early-bird pricing is in effect until April 28. Why Go avoids assert libraries Better alternatives to mocks Make writing tests fun! See pricing & reserve → log/slog provides some rather sophisticated capabilities around log levels. We’ll get into it, but the good news is, you don’t need to care about how sophisticated it can get, if you don’t care.


Logging common fields

It’s common that you’ll want to include certain attributes in all logs in an application or component. log/slog makes this pretty easy. Overview … Some attributes are common to many log calls. For example, you may wish to include the URL or trace identifier of a server request with all log events arising from the request. Rather than repeat the attribute with every log call, you can use Logger.With to construct a new Logger containing the attributes:


Handler configuration

The default slog handlers are quite configurable. Overview … Both TextHandler and JSONHandler can be configured with HandlerOptions. There are options for setting the minimum level (see Levels, below), displaying the source file and line of the log call, and modifying attributes before they are logged. While HandlerOptions only exposes three fields: AddSource bool Level Leveler ReplaceAttr func(groups []string, a Attr) Attr The last one provides an immense amount of flexibility, letting you filter, replace, or augment log key/value pairs as they are processed.

Get daily content like this in your inbox!

Subscribe