Consideration of an alternative approach when printing structs
fgimian opened this issue · comments
Hey there, thanks so much for your work on this handler, it's exactly what I was looking for.
One major difference between slog and zerolog which is reflected both in the slog.TextHandler
and the tint handler, is the handling of structs whereby both handlers either use the MarshalText
or essentially %v
to format structs while also adding quotes if there's a space and escaping interior quotes within the string returned.
Meanwhile, zerolog would log the JSON representation of the struct:
e.g.
2023-05-07 16:37:41 INF Getting extract callback stream entry={"isDir":false,"modTime":"2020-10-25T16:38:06+11:00","path":"WinSpy-1.0.3.7z","size":807471}
This is created used a simple marshal function as follows:
func (a ArchiveEntry) MarshalZerologObject(e *zerolog.Event) {
e.Str("path", a.Path)
e.Bool("isDir", a.IsDir)
e.Uint64("size", a.Size)
if a.ModTime != nil {
e.Time("modTime", *a.ModTime)
}
}
I was wondering whether it may be worth consider an option which uses MarshalJSON
instead and avoids the extra escaping and addition of quotes in the resultant output to provide similar output to zerolog?
I realise this would break the convention set out by slog and thus I'd understand if you were not a fan of this idea, but as it stands right now, I still am finding zerolog more practical.
Cheers
Fotis
Just to give you an idea of how I accomplished this locally while testing:
handler.go
...
func appendValue(buf *buffer, v slog.Value, quote bool) {
...
- if tm, ok := v.Any().(encoding.TextMarshaler); ok {
- data, err := tm.MarshalText()
- if err != nil {
- break
- }
- appendString(buf, string(data), quote)
- break
+ if jm, err := json.Marshal(v.Any()); err == nil {
+ appendString(buf, string(jm), false)
+ } else {
+ appendString(buf, fmt.Sprintf("%+v", v.Any()), false)
}
- appendString(buf, fmt.Sprint(v.Any()), quote)
...
And my example:
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
tintOptions := tint.Options{
Level: slog.LevelDebug,
TimeFormat: time.DateTime,
}
slog.SetDefault(slog.New(tintOptions.NewHandler(os.Stdout)))
person := Person{
Name: "John",
Age: 30,
}
slog.Info("This is the person", slog.Any("person", person))
}
Resulting in the following output:
As compared to:
And slog's TextHandler
looks like this:
Notice they've also adopted %+v
too so it may be worth switching that style either way 😄
Cheers
Fotis
Yes, this would definitely break with the conventions of TextHandler. Although what you describe is the default behavior of the JSONHandler.
However, I think there is a good equivalent solution to zerolog, the slog.LogValuer
.
func (p Person) LogValue() slog.Value {
return slog.GroupValue(
slog.String("name", p.Name),
slog.Int("age", A.age))
}
The output format is a little different though. ..., "person", p,...
would become person.name=John person.age=30
func (p Person) LogValue() slog.Value { return slog.GroupValue( slog.String("name", p.Name), slog.Int("age", A.age)) }
Actually, that's perfect and honestly nicer output than the JSON output as it is more human friendly. Thanks so much for the reply and tip, I wasn't aware of this method.
I'm happy to close this issue 😄
Fotis