Skip to content

Conversation

@brandur
Copy link
Contributor

@brandur brandur commented Oct 27, 2025

Here, replace with the utility SlogMessageOnlyHandler that we use in
example tests with a ReplaceAttr implementation instead so that we use
a standard TextHandler with a ReplaceAttr:

-		Logger: slog.New(&slogutil.SlogMessageOnlyHandler{Level: slog.LevelWarn}),
+		Logger: slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelWarn, ReplaceAttr: slogutil.NoLevelTimeJobID})),

The major benefit of this approach is that in case of an error (where
the error detail gets put in a key of the output log), we don't end up
hiding the detail and making the error difficult to debug without
removing the message-only handler.

It also has the nominal advantage of being a little more standard, i.e.
Uses normal slog handlers instead of a custom one, and is a little
more succinct to write.

Here, replace with the utility `SlogMessageOnlyHandler` that we use in
example tests with a `ReplaceAttr` implementation instead so that we use
a standard `TextHandler` with a `ReplaceAttr`:

``` diff
-		Logger: slog.New(&slogutil.SlogMessageOnlyHandler{Level: slog.LevelWarn}),
+		Logger: slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelWarn, ReplaceAttr: slogutil.NoLevelTimeJobID})),
```

The major benefit of this approach is that in case of an error (where
the error detail gets put in a key of the output log), we don't end up
hiding the detail and making the error difficult to debug without
removing the message-only handler.

It also has the nominal advantage of being a little more standard, i.e.
Uses normal `slog` handlers instead of a custom one, and is a little
more succinct to write.
@brandur brandur requested a review from bgentry October 27, 2025 07:45

riverClient, err := river.NewClient(riverpgxv5.New(dbPool), &river.Config{
Logger: slog.New(&slogutil.SlogMessageOnlyHandler{Level: slog.LevelWarn}),
Logger: slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelWarn, ReplaceAttr: slogutil.NoLevelTime})),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might prefer to still wrap this in a helper to a) avoid potential drift as examples are added/updated and b) allow some space for a comment about why this uses ReplaceAttr which most users won't want/need. Up to you though, I do see the advantage of not obscuring the example code behind more indirection.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, let's leave for now. I kinda like how this just shows raw options instead of hiding things in a helper, and it's really only three options it's configuring total, with 2/3 very standard. The comment on why ReplaceAttr is on slogutil.NoLevelTime in case anyone cares to jump-to and read it.

Comment on lines +58 to 62
// We have to use a specialized ReplacedAttr without level or
// timestamps to make test output reproducible, but in reality
// this would as simple as something like:
//
// return slog.NewJSONHandler(w, nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this is what I was getting at with context for why we have special attrs passed in, seems helpful here though it adds a few lines

@brandur
Copy link
Contributor Author

brandur commented Oct 27, 2025

Thanks!

@brandur brandur merged commit 92cdd61 into master Oct 27, 2025
14 checks passed
@brandur brandur deleted the brandur-no-job-id-level-or-time-replace-attr-2 branch October 27, 2025 21:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants