The better test alternative to slog.DiscardHandler

September 17, 2026

Last time I described slog.DiscardHandler as a viable way to silence logs in tests.

Now I want to tell you why I never use that option, and you probably shouldn’t, either.

But first a story.

The slog package was introduced with Go 1.21 in August of 2023. It didn’t yet have slog.DiscardHandler, so you had to roll your own. But that was easy:

logger := slog.New(slog.NewTextHandler(io.Discard, nil))

Easy as it was, it was also annoying. So in Go 1.24, slog.DiscardHandler was added, making it slightly easier.

logger := slog.New(slog.DiscardHandler)

But then in Go 1.25, something even better was introduced, and it’s what I always use now. But it came from a different place: testing.T.Output

What does T.Output do, and how does it relate to logging?

func (*T) Output

func (c *T) Output() io.Writer

Output returns a Writer that writes to the same test output stream as TB.Log. The output is indented like TB.Log lines, but Output does not add source locations or newlines. The output is internally line buffered, and a call to TB.Log or the end of the test will implicitly flush the buffer, followed by a newline. After a test function and all its parents return, neither Output nor the Write method may be called.

(Note the same method exists on testing.B, and testing.F as well).

In most cases, logging to t.Output() is always better than logging to the discard handler. In the usual case, the observable output is identical: Nothing.

But, in the case your test fails, the log output is now included in your test output. And that can be very valuable for debugging or diagnosing a test failure.

logger := slog.New(slog.NewTextHandler(t.Output(), nil))

What’s more, this trick isn’t limited to slog. You can use the exact same t.Output() writer for the log package, any third-party logging package, or any other writer that has nothing to do with logs at all!


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