log icon indicating copy to clipboard operation
log copied to clipboard

improve json handling of `slog.Group` fields

Open jatinn opened this issue 10 months ago • 0 comments

was checking this out as a way of improving displayed logs however noticed that slog.Group fields are represented differently for both the text and json formatter

Here is the output from the sample code included below

INFO charm text formatter req="[method=POST url=example.com]" map=map[foo:bar] list="[1 2 3]"
level=INFO msg="slog text formatter" req.method=POST req.url=example.com map=map[foo:bar] list="[1 2 3]"
{"level":"info","list":"[1 2 3]","map":"map[foo:bar]","msg":"charm json formatter","req":"[method=POST url=example.com]"}
{"level":"INFO","msg":"slog json formatter","req":{"method":"POST","url":"example.com"},"map":{"foo":"bar"},"list":[1,2,3]}

for the text formatter while req.method & req.url would be preferred it is still usable. however for the json output the representation is not a nested map, this becomes less valuable since parsing that data becomes more difficult as cannot use the same json parsing/selecting expressions with jq ex. to get the method i could use jq '.req.method' however that will no longer work with the charm json formatter

Update: seems to not be specific to group using map or list value also does not get encoded as json

Here is the sample code for reproduction

package main

import (
	"log/slog"
	"os"

	"github.com/charmbracelet/log"
)

func RemoveTime(groups []string, a slog.Attr) slog.Attr {
	if a.Key == slog.TimeKey && len(groups) == 0 {
		return slog.Attr{}
	}
	return a
}

func main() {
	ct := slog.New(log.NewWithOptions(os.Stdout, log.Options{}))
	cj := slog.New(log.NewWithOptions(os.Stdout, log.Options{Formatter: log.JSONFormatter}))
	st := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ReplaceAttr: RemoveTime}))
	sj := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ReplaceAttr: RemoveTime}))

	g := slog.Group("req",
		slog.String("method", "POST"),
		slog.String("url", "example.com"),
	)

	m := map[string]string{"foo": "bar"}
	l := []int{1, 2, 3}

	ct.Info("charm text formatter", g, "map", m, "list", l)
	st.Info("slog text formatter", g, "map", m, "list", l)
	cj.Info("charm json formatter", g, "map", m, "list", l)
	sj.Info("slog json formatter", g, "map", m, "list", l)
}

jatinn avatar Apr 20 '24 18:04 jatinn