Conversation
|
Hey, @StevenACoffman! |
abhinav
left a comment
There was a problem hiding this comment.
Prior comment notwithstanding, I think we can do this without bumping the Go version and just moving the two new functions into a Go 1.21 tagged file.
errtrace.go
Outdated
| func ErrAttr(err error) slog.Attr { | ||
| return slog.Any("error", err) | ||
| } |
There was a problem hiding this comment.
Thinking out loud:
The number of times "err" appears in this expression is quite high:
errtrace.ErrAttr(err)
What if we named this LogAttr?
slog.Default().Error("great sadness", errtrace.LogAttr(err))
|
|
||
| // Format writes the return trace for given error to the writer. | ||
| // The output takes a fromat similar to the following: | ||
| // The output takes a format similar to the following: |
errtrace.go
Outdated
| // | ||
| // slog.Default().Error("msg here", errtrace.ErrAttr(err)) | ||
| func ErrAttr(err error) slog.Attr { | ||
| return slog.Any("error", err) |
There was a problem hiding this comment.
The convention on log/slog seems to be to use "err" as the key for errors, not "error". This matches what Zap and Zerolog do as well.
There was a problem hiding this comment.
Actually, Zap uses "error" as the key: https://github.com/uber-go/zap/blob/master/error.go#L34
There was a problem hiding this comment.
The documentation also shows that "error" is the key in Zerolog https://github.com/rs/zerolog?tab=readme-ov-file#error-logging
package main
import (
"errors"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func main() {
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
err := errors.New("seems we have an error here")
log.Error().Err(err).Msg("")
}
// Output: {"level":"error","error":"seems we have an error here","time":1609085256}There was a problem hiding this comment.
In slog, the various proposals (and usage by the primary author jba) show using "error" as the key: golang/go#59364
func ErrAttr(err error) slog.Attr {
return slog.Any("error", err)
}
There was a problem hiding this comment.
Actually, Zap uses "error" as the key: https://github.com/uber-go/zap/blob/master/error.go#L34
Well, that's embarrassing for me. 😆
I don't know why I assumed "err". Will revert that change.
|
I've renamed the field and the function just to see how it looks. |
|
I would rather not use "err" as the key, but I don't feel strongly, and otherwise looks good to me! Thanks for a wonderful library. Zap uses "error" as the key: https://github.com/uber-go/zap/blob/master/error.go#L34 // Error is shorthand for the common idiom NamedError("error", err).
func Error(err error) Field {
return NamedError("error", err)
}Zerolog has documentation that also shows that "error" is the key in Zerolog https://github.com/rs/zerolog?tab=readme-ov-file#error-logging package main
import (
"errors"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func main() {
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
err := errors.New("seems we have an error here")
log.Error().Err(err).Msg("")
}
// Output: {"level":"error","error":"seems we have an error here","time":1609085256}In slog, the various proposals (and usage by the primary author jba) show using "error" as the key: golang/go#59364 Maybe there is some other examples that you have seen where that is not the case? |
|
Oops. Yeah, "error" is good. |
|
@abhinav is there anything remaining to get this merged? |
|
Hey @StevenACoffman, apologies, I had an update typed out but I never sent it. After mulling it over a bit, I don't think that the value should be structured: the majority use case will want to consume a string. For cases where folks want a structured object logged for errors, the logger APIs should provide enough control/overrides for how specific values are encoded—e.g. "encode errors using this function instead." But I want to confirm my thinking with others—you, @prashantv, @akshayjshah. Outside of that, if we agree on the approach, what remains before merging this PR is:
|
+1 on using string. Should we do something similar to zap here, |
I think not. In hindsight, that wasn't the most ideal choice we could've made there. |
Flexibility (at the logger level) is probably the right answer, but I've found the message-only errors useful when dealing with logs in aggregate (e.g., looking at a distribution of errors across log messages in a timeframe) where additional stacks/traces get quite noisy. On the flip side, once I've drilled into a specific log, then I find the additional information useful to understand where it came from. |
|
📝 Go 1.21 since #111. |
Co-authored-by: Abhinav Gupta <mail@abhinavg.net> Co-authored-by: Chris Bandy <bandy.chris@gmail.com> Signed-off-by: Steve Coffman <steve@khanacademy.org>
|
I rebased and squashed all these down over here: main...cbandy:errtrace:pr-97 |
a857d47 to
c729dda
Compare
|
@abhinav @prashantv I have force pushed @cbandy rebased squashed commit to my branch. |
Writing to a strings.Builder can never fail so we don't need the impossible error path. Use FormatString instead, which already does this.
abhinav
left a comment
There was a problem hiding this comment.
This looks good to me. I've added a couple test cases.
|
Fixing lint breakage here: #125 |
|
Will give it a day to see if @prashantv has any thoughts, but good to merge otherwise |
prashantv
left a comment
There was a problem hiding this comment.
Will drop LogAttr and merge.
errtrace.go
Outdated
| // When serialized with a slog-based logger, | ||
| // this will report an error return trace if the error has one, | ||
| // otherwise the original error message will be logged as-is. | ||
| func LogAttr(err error) slog.Attr { |
There was a problem hiding this comment.
Since the implementation of LogAttr is completely independent of errtrace, I'm not sure if it's valuable to add it here. I'll drop this for now, and we can always bring it back if we think there's value in this being part of the errtrace interface.
|
|
||
| // LogValue implements the [slog.LogValuer] interface. | ||
| func (e *errTrace) LogValue() slog.Value { | ||
| return slog.StringValue(FormatString(e)) |
There was a problem hiding this comment.
no changes needed, but wanted to consider whether this should return a string value, or a group with multiple fields for message, trace, etc.
I think a string makes sense to start, and allows callers to choose how they want the error encoded, e.g., in their equivalent of the LogAttr function.
|
Merged! Thanks for your patience, @StevenACoffman |
Follow-up to #97. We dropped the `LogAttr` function, so the changelog needs to be updated.
Follow-up to #97. We dropped the `LogAttr` function, so the changelog needs to be updated.
|
Wohoo! Thanks! |
| } | ||
| ``` | ||
|
|
||
| - Add `LogAttr` function to log any error with log/slog with the key "error". |
There was a problem hiding this comment.
looks like this changelog entry was left behind after the LogAttr func was removed
There was a problem hiding this comment.
We realized this after merging too, and fixed it in #126.
Thanks for catching either way!
Go 1.21 adds the
log/slogpackage and this PR adds convenience and compatibility with that package. Merging this PR would require dropping Go 1.20 support, which may not (yet!) be desired. If so, this PR can wait until that is desired.Each major Go release is supported until there are two newer major releases. Go 1.22 was released on February 6, 2024
so Go 1.20 is no longer supported.