slog.HandlerOptions

September 19, 2026

Now we’ll begin looking at HandlerOptions. Despite the Handler-centricity, this is something we care heavily about as mere users of the slog library.

Whenever you instantiate either slog.JSONHandler or slog.TextHandler, you have the option of providing a HandlerOptions object, to tweak the default handler behavior.

We’ll look at each of the config options, one at a time.

type HandlerOptions

type HandlerOptions struct {
	// AddSource causes the handler to compute the source code position
	// of the log statement and add a SourceKey attribute to the output.
	AddSource bool

The first option, AddSource, is honestly something I’ve never felt a need to use. But it’s good to know it’s there, in case you ever do.

When enabled, it adds a source key/value pair to each log, as you can see in this example:

func main() {
	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{AddSource: true}))
	logger.Info("Hello, 世界")
}

Output:

time=2009-11-10T23:00:00.000Z level=INFO source=/tmp/sandbox837883246/prog.go:12 msg="Hello, 世界"

The main reason I don’t use this, is that I generally include a full stack trace in my error objects, which then get logged:

2024-06-10T12:34:56Z ERROR Failed to connect to database
github.com/myorg/myapp/db.Connect
    /src/bootdev/course-draft-learn-logging/examples/errors/stacktrace/main.go:25
github.com/myorg/myapp/handlers.GetUser
    /src/bootdev/course-draft-learn-logging/examples/errors/stacktrace/main.go:40
github.com/myorg/myapp/server.ServeHTTP
    /src/bootdev/course-draft-learn-logging/examples/errors/stacktrace/main.go:55
net/http.serverHandler.ServeHTTP
    /usr/local/go/src/net/http/server.go:2887
net/http.(*conn).serve
    /usr/local/go/src/net/http/server.go:1952

If you’re interested in a much more detailed look at this particular strategy, as well as other logging and observability topics, I go into much more detail on this in the course I created for Boot.dev, Logging and Observability in Go. And if you use code JHALL when you sign up, you can get 25% off your first year!


Share this

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

Unsure? Browse the archive .

Related Content


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.


Handler level

Each handler has an optional Level value, which controls which logs to drop, and which to actually log: type HandlerOptions type HandlerOptions struct { … // Level reports the minimum record level that will be logged. // The handler discards records with lower levels. // If Level is nil, the handler assumes LevelInfo. // The handler calls Level.Level for each record processed; // to adjust the minimum level dynamically, use a LevelVar.

Get daily content like this in your inbox!

Subscribe