Overriding the default handler

April 8, 2026

Today I’m going to jump around a bit in the GoDoc, to talk about a topic I mentioned last time: how to override the default logger.

Overview

…

Setting a logger as the default with

slog.SetDefault(logger)

will cause the top-level functions like Info to use it. SetDefault also updates the default logger used by the log package, so that existing applications that use log.Printf and related functions will send log records to the logger’s handler without needing to be rewritten.

While I generally discourage use of the default logger, for reasons I’ll get into another day, if you have an application that uses either the default log handler or the default log/slog handler, you can at least control how it works!

file, err := os.Open("/var/log/myapp.log")
if err != nil {
  panic(err)
}
logger := slog.New(slog.NewJSONHandler(file, nil))
slog.SetDefault(logger)

// Some time later...

slog.Info("Interesting things are afoot!") // Written to /var/log/myapp.log

// Meanwhile, back at the farm...

log.Printf("And then he turned to me and said...") // Also written to /var/log/myapp.log

Share this

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

Unsure? Browse the archive .

Related Content


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.


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.


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