slog Handler methods

slog.Handler is an interface type. We’re going to look at each method on that interface in turn. Understanding these methods is valuable if you’re ever implementing your own handler. If you’re only using slog to produce logs using existing handlers, you could skip this part… though maybe you’ll learn something interesting anyway! type Handler type Handler interface { // Enabled reports whether the handler handles records at the given level. // The handler ignores records whose level is lower.


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.


slog.Group

The one Attr constructor not yet covered: func Group func Group(key string, args ...any) Attr Group returns an Attr for a Group Value. The first argument is the key; the remaining arguments are converted to Attrs as in Logger.Log. Use Group to collect several key-value pairs under a single key on a log line, or as the result of LogValue in order to log a single value as multiple Attrs.


The Attr type

Other than logger methods, the slog.Attr type is probably the thing you’ll use the most in your use of the slog package: Types type Attr type Attr struct { Key string Value Value } An Attr is a key-value pair. This type has several type-specific constructors, too, but we’re going to do an abbrevaited look at them, because they’re highly repetitive. func Any func Any(key string, value any) Attr Any returns an Attr for the supplied value.


Backward compatibility

We’ve already discussed when the default log/slog Logger also serves as the default log Logger. But what if you want to send log logs to a log/slog Logger, without using the global defaults? Introducing NewLogLogger! func NewLogLogger func NewLogLogger(h Handler, level Level) *log.Logger NewLogLogger returns a new log.Logger such that each call to its Output method dispatches a Record to the specified handler. The logger acts as a bridge from the older log API to newer structured logging handlers.


The global logger

I’m going to do something I haven’t done before in these stdlib tours, and that is skip over a huge section of the GoDoc. That’s becase a huge section here is entirely redundant with what comes later: func Debug func Debug(msg string, args ...any) Debug calls Logger.Debug on the default logger. And we have virtually identical entries for each of the following: DebugContext Error ErrorContext Info InfoContext Log LogAttrs Warn WarnContext Each of these is simply an alias to the identically named method on the default logger.

Subscribe to Boldly Go: Daily

Every day I'll send you advice to improve your understanding of Go. Don't miss out! I will respect your inbox, and honor my privacy policy.

Unsure? Browse the archive.


slog Constants

We’ve made it through the overview of the slog documentation. Now it’s time to get down and dirty! First up, constants! There are four, and they all define built-in attribute keys: Constants const ( // TimeKey is the key used by the built-in handlers for the time // when the log method is called. The associated Value is a [time.Time]. TimeKey = "time" // LevelKey is the key used by the built-in handlers for the level // of the log call.


Concurrent logging

One last note in the performance section… How does logging work in a concurrent system? Performance considerations … The built-in handlers acquire a lock before calling io.Writer.Write to ensure that exactly one Record is written at a time in its entirety. Although each log record has a timestamp, the built-in handlers do not use that time to sort the written records. User-defined handlers are responsible for their own locking and sorting.


Back after an unannounced absence

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 …


Lazy attribute evaluation for JSONHandler

As I was writing yesterday’s post, a portion of the GoDoc confused me. I’ve now spent over 3 hours with Claude trying to parse the prose grammatically, build test cases, and make general sense of it. I think I finally have… Here’s hoping! So, yesterday we saw how you can lazy-evaluate some values when using TextHandler. But the proposed solution (pass a fmt.Stringer rather than a literal string) has other, likely uninintended, consequences if you’re using JSONHandler: