More with Groups

April 29, 2026

Organizing log key/value pairs by group is a nice way to organize your logging data, but what about organizing the way your application groups data?

Maybe you want all logs created by a particular code path to be grouped together. How can you accomplish this?

Enter WithGroup

Groups

Use Logger.WithGroup to qualify all of a Logger’s output with a group name. Calling WithGroup on a Logger results in a new Logger with the same Handler as the original, but with all its attributes qualified by the group name.

This can help prevent duplicate attribute keys in large systems, where subsystems might use the same keys. Pass each subsystem a different Logger with its own group name so that potential duplicates are qualified:

logger := slog.Default().With("id", systemID)
parserLogger := logger.WithGroup("parser")
parseInput(input, parserLogger)

When parseInput logs with parserLogger, its keys will be qualified with “parser”, so even if it uses the common key “id”, the log line will have distinct keys.

So we’ve already seen how you can use With to add attributes to all logs in a logger:

logger = logger.With("request_id", req.Get("ID"))

WithGroup works the same way, but any additional keys added to the returned logger are added to the group:

func FooMiddleware(logger *slog.Logger) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        logger = logger.With("foo_middleware")

        /* ... */
        logger.Info("Handling request", "method", r.Method)

        /* ... */
        logger.Debug("Checking credentials")

        /* ... */
        if err != nil {
            logger.Error("foo failed", "error", err")
        }
    }
}

In this above example, all three logs add their keys inside the “foo_middleware” group, without having to specify the group more than once.


Share this

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

Unsure? Browse the archive .

Related Content


Values

TIL Logger.LogAttrs is a thing! But what is that thing?? Yesterday I mentioned that using an slog.Attr can be marginally more efficient than using naked key/value pairs in a log call. While true, that glosses over what is likely to be a much more impactful performance consideration in certain applications… Attrs and Values … The value part of an Attr is a type called Value. Like an [any], a Value can hold any Go value, but it can represent typical values, including all numbers and strings, without an allocation.


Attrs

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.


Contexts

[**Idiomatic Testing in Go**](/idiomatic-testing/) starts TOMORROW! My my, how time flies! There are still a few seats available, and there’s still time to sign up. Learn how to get the most out of the tests in your Go app! One feature I often see overlooked capability of the log/slog package, is to extract log key/value pairs from context: Contexts Some handlers may wish to include information from the context.Context that is available at the call site.

Get daily content like this in your inbox!

Subscribe