diff --git a/handler.go b/handler.go index af76de7..39635f2 100644 --- a/handler.go +++ b/handler.go @@ -403,3 +403,23 @@ type jsonLog struct { Attrs map[string]any `json:"attrs,omitempty"` Pid string `json:"pid,omitempty"` } + +func (j jsonLog) MarshalJSON() ([]byte, error) { + a := map[string]any{} + for key, value := range j.Attrs { + if err, ok := value.(error); ok { + a[key] = err.Error() + } else { + a[key] = value + } + } + + return json.Marshal(jsonLog{ + Level: j.Level, + Time: j.Time, + Message: j.Message, + Group: j.Group, + Attrs: a, + Pid: j.Pid, + }) +} diff --git a/handler_test.go b/handler_test.go index cc26864..65565d4 100644 --- a/handler_test.go +++ b/handler_test.go @@ -3,6 +3,7 @@ package shandler_test import ( "bytes" "context" + "errors" "fmt" "io" "log/slog" @@ -249,6 +250,37 @@ func TestErrorTags(t *testing.T) { assert.Contains(t, stderr.String(), fmt.Sprintf("[ERROR] %s - test error_id=", now)) } +func TestSlogAnyError(t *testing.T) { + now := time.Now().Format(time.TimeOnly) + tests := []struct { + name string + opts []handler.HandlerOption + expected string + }{ + { + name: "text", + opts: []handler.HandlerOption{}, + expected: fmt.Sprintf(`[ERROR] %s - test err=big error`, now), + }, + { + name: "json", + opts: []handler.HandlerOption{handler.WithJSON()}, + expected: fmt.Sprintf(`{"level":"ERROR","time":"%s","message":"test","attrs":{"err":"big error"}}`, now), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var stderr bytes.Buffer + tt.opts = append(tt.opts, handler.WithStdErr(&stderr)) + logger := slog.New(handler.NewHandler(tt.opts...)) + err := errors.New("big error") + logger.Error("test", slog.Any("err", err)) + actual := strings.TrimRight(stderr.String(), "\n") + assert.Equal(t, tt.expected, actual) + }) + } +} + func BenchmarkHandlers(b *testing.B) { var stdout bytes.Buffer bt := []struct {