Attrs

May 5, 2026

We’ve been talking about key/value pairs. The log/slog package has a name for these: Attr (short for “attribute”). And there’s more than one way to build an attribute:

Attrs and Values

An Attr is a key-value pair. The Logger output methods accept Attrs as well as alternating keys and values. The statement

slog.Info("hello", slog.Int("count", 3))

behaves the same as

slog.Info("hello", "count", 3)

There are convenience constructors for Attr such as Int, String, and Bool for common types, as well as the function Any for constructing Attrs of any type.

You might wonder why there are two ways to do this. I know I do!

I can guess, though: Allowing both convenience, and precision.

Passing raw key/value pairs is generally more convenient:

slog.Info("request served", "url", req.URL.String(), "method", req.Method, ...)

But it’s also error-prone. Particularly with long lists of attributes, it’s easy to miss either a key or a value, which can trigger all sorts of strange behavior!

There may also be times when you want to pass around attributes, and that’s often easier as discrete slog.Attr values than as a slice of key/value pairs.

And finally, there may be a marginal performance gain by using one of the type-specific attribute constructors, since it avoids a type assertion at runtime—not a concern most of us will ever need to worry about, of course.

So should you prever key/value pairs ("key", value), or the explicit attribute constructors (slog.Int("key", value)). Your call. I generally prefer the explicit constructors, because it’s more explicit, and less error prone—particularly if the log ever grows more key/value pairs over time. But: I would never hold up a code review because somebody else chose the other format instead!


Share this

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

Unsure? Browse the archive .

Related Content


Attribute equality

func (Attr) Equal func (a Attr) Equal(b Attr) bool Equal reports whether a and b have equal keys and values. That’s a pretty obvious, and opaque statement. How is equality determined? We have to look at the source to determine: func (a Attr) Equal(b Attr) bool { return a.Key == b.Key && a.Value.Equal(b.Value) } Okay, so it returns true if the keys are equal (that’s simple—they’re just strings) AND if the values are equal.


slog.GroupAttrs

Last time we looked at slog.Group, which accepts the variadic ...any as arguments. But there’s also a version that accepts slog.Attr values: func GroupAttrs func GroupAttrs(key string, attrs ...Attr) Attr GroupAttrs returns an Attr for a Group Value consisting of the given Attrs. GroupAttrs is a more efficient version of Group that accepts only Attr values. But notice there’s no mixed form of this, like there is for the standard logging functions.


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