Back after an unannounced absence

June 16, 2026

Hey everyone… I dropped the ball! A combination of unexpected family events, prepping for a conference, and some travel, meant I haven’t been writing for much longer than I like. But I’m back!

So where were we? Oh that’s right… performance considerations with log/slog. We had looked at using the fmt.Stringer interface to avoid eager processing with slog.TextHandler. But let’s now look at a more general solution:

Performance considerations

You can also use the LogValuer interface to avoid unnecessary work in disabled log calls. Say you need to log some expensive value:

slog.Debug("frobbing", "value", computeExpensiveValue(arg))

Even if this line is disabled, computeExpensiveValue will be called. To avoid that, define a type implementing LogValuer:

type expensive struct { arg int }

func (e expensive) LogValue() slog.Value {
    return slog.AnyValue(computeExpensiveValue(e.arg))
}

Then use a value of that type in log calls:

slog.Debug("frobbing", "value", expensive{arg})

Now computeExpensiveValue will only be called when the line is enabled.

Let’s adapt the earlier url.URL example to use slog.LogValuer, for use with all handlers:

type myURL struct {
  url.URL
}

func (u myURL) LogValue() slog.Value {
  return slog.StringValue(u.URL.String())
}

This does, of course, mean you need to convert to your custom type before logging:

slog.Info("starting request", "url", myURL{*r.URL})

Share this

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

Unsure? Browse the archive .

Related Content


Customizing a type's logging behavior

You may find cases where you wish to control how a value is logged, differently than how it’s used in other contexts. The log/slog package gives you a lot of flexibility in this regard, for custom types: Customizing a type’s logging behavior If a type implements the LogValuer interface, the Value returned from its LogValue method is used for logging. You can use this to control how values of the type appear in logs.


slog.HandlerOptions

Now we’ll begin looking at HandlerOptions. Despite the Handler-centricity, this is something we care heavily about as mere users of the slog library. Whenever you instantiate either slog.JSONHandler or slog.TextHandler, you have the option of providing a HandlerOptions object, to tweak the default handler behavior. We’ll look at each of the config options, one at a time. type HandlerOptions type HandlerOptions struct { // AddSource causes the handler to compute the source code position // of the log statement and add a SourceKey attribute to the output.


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.

Get daily content like this in your inbox!

Subscribe