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})