1package log
2
3import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7 "time"
8)
9
10func (l *Logger) jsonFormatter(keyvals ...interface{}) {
11 jw := &jsonWriter{w: &l.b}
12 jw.start()
13
14 i := 0
15 for i < len(keyvals) {
16 switch kv := keyvals[i].(type) {
17 case slogAttr:
18 l.jsonFormatterRoot(jw, kv.Key, kv.Value)
19 i++
20 default:
21 if i+1 < len(keyvals) {
22 l.jsonFormatterRoot(jw, keyvals[i], keyvals[i+1])
23 }
24 i += 2
25 }
26 }
27
28 jw.end()
29 l.b.WriteRune('\n')
30}
31
32func (l *Logger) jsonFormatterRoot(jw *jsonWriter, key, value any) {
33 switch key {
34 case TimestampKey:
35 if t, ok := value.(time.Time); ok {
36 jw.objectItem(TimestampKey, t.Format(l.timeFormat))
37 }
38 case LevelKey:
39 if level, ok := value.(Level); ok {
40 jw.objectItem(LevelKey, level.String())
41 }
42 case CallerKey:
43 if caller, ok := value.(string); ok {
44 jw.objectItem(CallerKey, caller)
45 }
46 case PrefixKey:
47 if prefix, ok := value.(string); ok {
48 jw.objectItem(PrefixKey, prefix)
49 }
50 case MessageKey:
51 if msg := value; msg != nil {
52 jw.objectItem(MessageKey, fmt.Sprint(msg))
53 }
54 default:
55 l.jsonFormatterItem(jw, key, value)
56 }
57}
58
59func (l *Logger) jsonFormatterItem(jw *jsonWriter, key, value any) {
60 switch k := key.(type) {
61 case fmt.Stringer:
62 jw.objectKey(k.String())
63 case error:
64 jw.objectKey(k.Error())
65 default:
66 jw.objectKey(fmt.Sprint(k))
67 }
68 switch v := value.(type) {
69 case error:
70 jw.objectValue(v.Error())
71 case slogLogValuer:
72 l.writeSlogValue(jw, v.LogValue())
73 case slogValue:
74 l.writeSlogValue(jw, v.Resolve())
75 case fmt.Stringer:
76 jw.objectValue(v.String())
77 default:
78 jw.objectValue(v)
79 }
80}
81
82func (l *Logger) writeSlogValue(jw *jsonWriter, v slogValue) {
83 switch v.Kind() {
84 case slogKindGroup:
85 jw.start()
86 for _, attr := range v.Group() {
87 l.jsonFormatterItem(jw, attr.Key, attr.Value)
88 }
89 jw.end()
90 default:
91 jw.objectValue(v.Any())
92 }
93}
94
95type jsonWriter struct {
96 w *bytes.Buffer
97 d int
98}
99
100func (w *jsonWriter) start() {
101 w.w.WriteRune('{')
102 w.d = 0
103}
104
105func (w *jsonWriter) end() {
106 w.w.WriteRune('}')
107}
108
109func (w *jsonWriter) objectItem(key string, value any) {
110 w.objectKey(key)
111 w.objectValue(value)
112}
113
114func (w *jsonWriter) objectKey(key string) {
115 if w.d > 0 {
116 w.w.WriteRune(',')
117 }
118 w.d++
119
120 pos := w.w.Len()
121 err := w.writeEncoded(key)
122 if err != nil {
123 w.w.Truncate(pos)
124 w.w.WriteString(`"invalid key"`)
125 }
126 w.w.WriteRune(':')
127}
128
129func (w *jsonWriter) objectValue(value any) {
130 pos := w.w.Len()
131 err := w.writeEncoded(value)
132 if err != nil {
133 w.w.Truncate(pos)
134 w.w.WriteString(`"invalid value"`)
135 }
136}
137
138func (w *jsonWriter) writeEncoded(v any) error {
139 e := json.NewEncoder(w.w)
140 e.SetEscapeHTML(false)
141 if err := e.Encode(v); err != nil {
142 return err
143 }
144
145 // trailing \n added by json.Encode
146 b := w.w.Bytes()
147 if len(b) > 0 && b[len(b)-1] == '\n' {
148 w.w.Truncate(w.w.Len() - 1)
149 }
150 return nil
151}