Contexts

May 4, 2026

[**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. One example of such information is the identifier for the current span when tracing is enabled.

The Logger.Log and Logger.LogAttrs methods take a context as a first argument, as do their corresponding top-level functions.

Although the convenience methods on Logger (Info and so on) and the corresponding top-level functions do not take a context, the alternatives ending in “Context” do. For example,

slog.InfoContext(ctx, "message")

It is recommended to pass a context to an output method if one is available.

Why is this frequently overlooked?

Well, one reason is that the handlers bundled in the standard library do not, by default, extract any key/value pairs from the context!

So while the official recommendation is to pass context, when available, unless you’re using a third-party logging handler, you’re passing the context to a black hole.

Is it still good advice to pass context? Yes, it probably is. When you’re writing new code,the cost of passing context is negligible. But at some possible future date, when you add a custom or third-party log handler that requires context, retrofitting your entire application can be quite painful! (Ask me how I know….)


Share this

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

Unsure? Browse the archive .

Related Content


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.


When not to use context values

Storing values in a context is quite flexible, but comes without strict type safety, and obscures the API. So what can we do about this? First, for type safety, we’re limited to runtime assertions: userID := ctx.Value(userIDKey).(string) But this can panic if we ever get an unexpected value (or even nil). So to make it safer, we can use the two-variable assignment form, which yields a bool indicating success: userID, ok := ctx.


Expanding errors with ReplaceAttr

Let’s look at one other example where I reach for ReplaceAttr regularly: Logging additional error detail. (I talk more about this approach in my Boot.Dev course Learn Logging and Observability in Go (see below for a discount code).) For this technique to be meaningful, I first need errors that contain additional details. And this can take many forms, but probably the most ubiquitous form is an error that contains a stack trace, such as those produced by the popular package github.

Get daily content like this in your inbox!

Subscribe