slog.DiscardHandler

September 14, 2026

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:

func TestFoo(t *testing.T) {
  logger := slog.New(slog.DiscardHandler)
  got, err := Foo(t.Context(), logger)
  if err != nil {
    t.Fatal(err)
  }
  if want := 42; got != want {
    t.Errorf("Foo returned an unexpected value: %v", got)
  }
}

The other use case would be a simple way to disable logging based on configuration or runtime state.

func initLogger() *slog.Logger {
  var handler slog.Handler
  if os.Getenv("DISABLE_LOGGING") != "" {
    handler = slog.DiscardHandler
  } else {
    handler = slog.NewJSONHandler(os.Stderr, nil)
  }
  return slog.New(handler)
}

Share this

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

Unsure? Browse the archive .

Related Content


The better test alternative to slog.DiscardHandler

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.


Expanding errors with ReplaceAttr

Let’s look at one other example where I reach for ReplaceAttr regularly: Logging additional error detail. (I talk more about this approach in my Boot.Dev course Learn Logging and Observability in Go (see below for a discount code).) For this technique to be meaningful, I first need errors that contain additional details. And this can take many forms, but probably the most ubiquitous form is an error that contains a stack trace, such as those produced by the popular package github.


Test logging with ReplaceAttr

Today’s topic is probably the most interesting, subtle, and complicated of the three HandlerOptions fields: ReplaceAttr. type HandlerOptions type HandlerOptions struct { … // ReplaceAttr is called to rewrite each non-group attribute before it is logged. // The attribute's value has been resolved (see [Value.Resolve]). // If ReplaceAttr returns a zero Attr, the attribute is discarded. // // The built-in attributes with keys "time", "level", "source", and "msg" // are passed to this function, except that time is omitted // if zero, and source is omitted if AddSource is false.

Get daily content like this in your inbox!

Subscribe