Logging common fields

April 16, 2026

It’s common that you’ll want to include certain attributes in all logs in an application or component. log/slog makes this pretty easy.

Overview

Some attributes are common to many log calls. For example, you may wish to include the URL or trace identifier of a server request with all log events arising from the request. Rather than repeat the attribute with every log call, you can use Logger.With to construct a new Logger containing the attributes:

logger2 := logger.With("url", r.URL)

The arguments to With are the same key-value pairs used in Logger.Info. The result is a new Logger with the same handler as the original, but additional attributes that will appear in the output of every call.

Put another way:

logger.Error("oh noes!", "foo", "bar")

and

logger = logger.With("foo", "bar")
logger.Error("oh noes!")

produce the same log output. The difference is, that in the second form, you can re-use the same logger for many logs, without repeating the common attribute:

func run(logger *slog.Logger) {
  logger = logger.With("key", "value")
  doFoo(logger)
  doBar(logger)
}

func doFoo(logger *slog.Logger) {
  /* ... */
  logger.Info("Did foo")
}

func doBar(logger *slog.Logger) {
  /* ... */
  logger.Info("Did bar")
}

This will log:

time=... level=INFO msg="Did foo" key=value
time=... level=INFO msg="Did bar" key=value

Share this

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

Unsure? Browse the archive .

Related Content


Log levels

I'm launching a new live course: **Idiomatic Testing in Go**. The course begins May 5. Early-bird pricing is in effect until April 28. Why Go avoids assert libraries Better alternatives to mocks Make writing tests fun! See pricing & reserve → log/slog provides some rather sophisticated capabilities around log levels. We’ll get into it, but the good news is, you don’t need to care about how sophisticated it can get, if you don’t care.


Handler configuration

The default slog handlers are quite configurable. Overview … Both TextHandler and JSONHandler can be configured with HandlerOptions. There are options for setting the minimum level (see Levels, below), displaying the source file and line of the log call, and modifying attributes before they are logged. While HandlerOptions only exposes three fields: AddSource bool Level Leveler ReplaceAttr func(groups []string, a Attr) Attr The last one provides an immense amount of flexibility, letting you filter, replace, or augment log key/value pairs as they are processed.


Chosing an slog handler

log/slog ships with two default handlers: the TextHandler and the JSONHandler. Overview … For more control over the output format, create a logger with a different handler. This statement uses New to create a new logger with a TextHandler that writes structured records in text form to standard error: logger := slog.New(slog.NewTextHandler(os.Stderr, nil)) TextHandler output is a sequence of key=value pairs, easily and unambiguously parsed by machine. This statement: logger.Info("hello", "count", 3) produces this output:

Get daily content like this in your inbox!

Subscribe