Source file
src/log/slog/json_handler.go
1
2
3
4
5 package slog
6
7 import (
8 "bytes"
9 "context"
10 "encoding/json"
11 "errors"
12 "fmt"
13 "io"
14 "log/slog/internal/buffer"
15 "strconv"
16 "sync"
17 "time"
18 "unicode/utf8"
19 )
20
21
22
23 type JSONHandler struct {
24 *commonHandler
25 }
26
27
28
29
30 func NewJSONHandler(w io.Writer, opts *HandlerOptions) *JSONHandler {
31 if opts == nil {
32 opts = &HandlerOptions{}
33 }
34 return &JSONHandler{
35 &commonHandler{
36 json: true,
37 w: w,
38 opts: *opts,
39 mu: &sync.Mutex{},
40 },
41 }
42 }
43
44
45
46 func (h *JSONHandler) Enabled(_ context.Context, level Level) bool {
47 return h.commonHandler.enabled(level)
48 }
49
50
51
52 func (h *JSONHandler) WithAttrs(attrs []Attr) Handler {
53 return &JSONHandler{commonHandler: h.commonHandler.withAttrs(attrs)}
54 }
55
56 func (h *JSONHandler) WithGroup(name string) Handler {
57 return &JSONHandler{commonHandler: h.commonHandler.withGroup(name)}
58 }
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 func (h *JSONHandler) Handle(_ context.Context, r Record) error {
91 return h.commonHandler.handle(r)
92 }
93
94
95 func appendJSONTime(s *handleState, t time.Time) {
96 if y := t.Year(); y < 0 || y >= 10000 {
97
98
99 s.appendError(errors.New("time.Time year outside of range [0,9999]"))
100 }
101 s.buf.WriteByte('"')
102 *s.buf = t.AppendFormat(*s.buf, time.RFC3339Nano)
103 s.buf.WriteByte('"')
104 }
105
106 func appendJSONValue(s *handleState, v Value) error {
107 switch v.Kind() {
108 case KindString:
109 s.appendString(v.str())
110 case KindInt64:
111 *s.buf = strconv.AppendInt(*s.buf, v.Int64(), 10)
112 case KindUint64:
113 *s.buf = strconv.AppendUint(*s.buf, v.Uint64(), 10)
114 case KindFloat64:
115
116
117
118 if err := appendJSONMarshal(s.buf, v.Float64()); err != nil {
119 return err
120 }
121 case KindBool:
122 *s.buf = strconv.AppendBool(*s.buf, v.Bool())
123 case KindDuration:
124
125 *s.buf = strconv.AppendInt(*s.buf, int64(v.Duration()), 10)
126 case KindTime:
127 s.appendTime(v.Time())
128 case KindAny:
129 a := v.Any()
130 _, jm := a.(json.Marshaler)
131 if err, ok := a.(error); ok && !jm {
132 s.appendString(err.Error())
133 } else {
134 return appendJSONMarshal(s.buf, a)
135 }
136 default:
137 panic(fmt.Sprintf("bad kind: %s", v.Kind()))
138 }
139 return nil
140 }
141
142 func appendJSONMarshal(buf *buffer.Buffer, v any) error {
143
144 var bb bytes.Buffer
145 enc := json.NewEncoder(&bb)
146 enc.SetEscapeHTML(false)
147 if err := enc.Encode(v); err != nil {
148 return err
149 }
150 bs := bb.Bytes()
151 buf.Write(bs[:len(bs)-1])
152 return nil
153 }
154
155
156
157
158
159
160 func appendEscapedJSONString(buf []byte, s string) []byte {
161 char := func(b byte) { buf = append(buf, b) }
162 str := func(s string) { buf = append(buf, s...) }
163
164 start := 0
165 for i := 0; i < len(s); {
166 if b := s[i]; b < utf8.RuneSelf {
167 if safeSet[b] {
168 i++
169 continue
170 }
171 if start < i {
172 str(s[start:i])
173 }
174 char('\\')
175 switch b {
176 case '\\', '"':
177 char(b)
178 case '\n':
179 char('n')
180 case '\r':
181 char('r')
182 case '\t':
183 char('t')
184 default:
185
186 str(`u00`)
187 char(hex[b>>4])
188 char(hex[b&0xF])
189 }
190 i++
191 start = i
192 continue
193 }
194 c, size := utf8.DecodeRuneInString(s[i:])
195 if c == utf8.RuneError && size == 1 {
196 if start < i {
197 str(s[start:i])
198 }
199 str(`\ufffd`)
200 i += size
201 start = i
202 continue
203 }
204
205
206
207
208
209
210
211 if c == '\u2028' || c == '\u2029' {
212 if start < i {
213 str(s[start:i])
214 }
215 str(`\u202`)
216 char(hex[c&0xF])
217 i += size
218 start = i
219 continue
220 }
221 i += size
222 }
223 if start < len(s) {
224 str(s[start:])
225 }
226 return buf
227 }
228
229 const hex = "0123456789abcdef"
230
231
232
233
234
235
236
237
238
239 var safeSet = [utf8.RuneSelf]bool{
240 ' ': true,
241 '!': true,
242 '"': false,
243 '#': true,
244 '$': true,
245 '%': true,
246 '&': true,
247 '\'': true,
248 '(': true,
249 ')': true,
250 '*': true,
251 '+': true,
252 ',': true,
253 '-': true,
254 '.': true,
255 '/': true,
256 '0': true,
257 '1': true,
258 '2': true,
259 '3': true,
260 '4': true,
261 '5': true,
262 '6': true,
263 '7': true,
264 '8': true,
265 '9': true,
266 ':': true,
267 ';': true,
268 '<': true,
269 '=': true,
270 '>': true,
271 '?': true,
272 '@': true,
273 'A': true,
274 'B': true,
275 'C': true,
276 'D': true,
277 'E': true,
278 'F': true,
279 'G': true,
280 'H': true,
281 'I': true,
282 'J': true,
283 'K': true,
284 'L': true,
285 'M': true,
286 'N': true,
287 'O': true,
288 'P': true,
289 'Q': true,
290 'R': true,
291 'S': true,
292 'T': true,
293 'U': true,
294 'V': true,
295 'W': true,
296 'X': true,
297 'Y': true,
298 'Z': true,
299 '[': true,
300 '\\': false,
301 ']': true,
302 '^': true,
303 '_': true,
304 '`': true,
305 'a': true,
306 'b': true,
307 'c': true,
308 'd': true,
309 'e': true,
310 'f': true,
311 'g': true,
312 'h': true,
313 'i': true,
314 'j': true,
315 'k': true,
316 'l': true,
317 'm': true,
318 'n': true,
319 'o': true,
320 'p': true,
321 'q': true,
322 'r': true,
323 's': true,
324 't': true,
325 'u': true,
326 'v': true,
327 'w': true,
328 'x': true,
329 'y': true,
330 'z': true,
331 '{': true,
332 '|': true,
333 '}': true,
334 '~': true,
335 '\u007f': true,
336 }
337
View as plain text