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 func (h *JSONHandler) Handle(_ context.Context, r Record) error {
89 return h.commonHandler.handle(r)
90 }
91
92
93 func appendJSONTime(s *handleState, t time.Time) {
94 if y := t.Year(); y < 0 || y >= 10000 {
95
96
97 s.appendError(errors.New("time.Time year outside of range [0,9999]"))
98 }
99 s.buf.WriteByte('"')
100 *s.buf = t.AppendFormat(*s.buf, time.RFC3339Nano)
101 s.buf.WriteByte('"')
102 }
103
104 func appendJSONValue(s *handleState, v Value) error {
105 switch v.Kind() {
106 case KindString:
107 s.appendString(v.str())
108 case KindInt64:
109 *s.buf = strconv.AppendInt(*s.buf, v.Int64(), 10)
110 case KindUint64:
111 *s.buf = strconv.AppendUint(*s.buf, v.Uint64(), 10)
112 case KindFloat64:
113
114
115
116 if err := appendJSONMarshal(s.buf, v.Float64()); err != nil {
117 return err
118 }
119 case KindBool:
120 *s.buf = strconv.AppendBool(*s.buf, v.Bool())
121 case KindDuration:
122
123 *s.buf = strconv.AppendInt(*s.buf, int64(v.Duration()), 10)
124 case KindTime:
125 s.appendTime(v.Time())
126 case KindAny:
127 a := v.Any()
128 _, jm := a.(json.Marshaler)
129 if err, ok := a.(error); ok && !jm {
130 s.appendString(err.Error())
131 } else {
132 return appendJSONMarshal(s.buf, a)
133 }
134 default:
135 panic(fmt.Sprintf("bad kind: %s", v.Kind()))
136 }
137 return nil
138 }
139
140 type jsonEncoder struct {
141 buf *bytes.Buffer
142
143 json *json.Encoder
144 }
145
146 var jsonEncoderPool = &sync.Pool{
147 New: func() any {
148 enc := &jsonEncoder{
149 buf: new(bytes.Buffer),
150 }
151 enc.json = json.NewEncoder(enc.buf)
152 enc.json.SetEscapeHTML(false)
153 return enc
154 },
155 }
156
157 func appendJSONMarshal(buf *buffer.Buffer, v any) error {
158 j := jsonEncoderPool.Get().(*jsonEncoder)
159 defer func() {
160
161 const maxBufferSize = 16 << 10
162 if j.buf.Cap() > maxBufferSize {
163 return
164 }
165 j.buf.Reset()
166 jsonEncoderPool.Put(j)
167 }()
168
169 if err := j.json.Encode(v); err != nil {
170 return err
171 }
172
173 bs := j.buf.Bytes()
174 buf.Write(bs[:len(bs)-1])
175 return nil
176 }
177
178
179
180
181
182
183 func appendEscapedJSONString(buf []byte, s string) []byte {
184 char := func(b byte) { buf = append(buf, b) }
185 str := func(s string) { buf = append(buf, s...) }
186
187 start := 0
188 for i := 0; i < len(s); {
189 if b := s[i]; b < utf8.RuneSelf {
190 if safeSet[b] {
191 i++
192 continue
193 }
194 if start < i {
195 str(s[start:i])
196 }
197 char('\\')
198 switch b {
199 case '\\', '"':
200 char(b)
201 case '\n':
202 char('n')
203 case '\r':
204 char('r')
205 case '\t':
206 char('t')
207 default:
208
209 str(`u00`)
210 char(hex[b>>4])
211 char(hex[b&0xF])
212 }
213 i++
214 start = i
215 continue
216 }
217 c, size := utf8.DecodeRuneInString(s[i:])
218 if c == utf8.RuneError && size == 1 {
219 if start < i {
220 str(s[start:i])
221 }
222 str(`\ufffd`)
223 i += size
224 start = i
225 continue
226 }
227
228
229
230
231
232
233
234 if c == '\u2028' || c == '\u2029' {
235 if start < i {
236 str(s[start:i])
237 }
238 str(`\u202`)
239 char(hex[c&0xF])
240 i += size
241 start = i
242 continue
243 }
244 i += size
245 }
246 if start < len(s) {
247 str(s[start:])
248 }
249 return buf
250 }
251
252 const hex = "0123456789abcdef"
253
254
255
256
257
258
259
260
261
262 var safeSet = [utf8.RuneSelf]bool{
263 ' ': true,
264 '!': true,
265 '"': false,
266 '#': true,
267 '$': true,
268 '%': true,
269 '&': true,
270 '\'': true,
271 '(': true,
272 ')': true,
273 '*': true,
274 '+': true,
275 ',': true,
276 '-': true,
277 '.': true,
278 '/': true,
279 '0': true,
280 '1': true,
281 '2': true,
282 '3': true,
283 '4': true,
284 '5': true,
285 '6': true,
286 '7': true,
287 '8': true,
288 '9': true,
289 ':': true,
290 ';': true,
291 '<': true,
292 '=': true,
293 '>': true,
294 '?': true,
295 '@': true,
296 'A': true,
297 'B': true,
298 'C': true,
299 'D': true,
300 'E': true,
301 'F': true,
302 'G': true,
303 'H': true,
304 'I': true,
305 'J': true,
306 'K': true,
307 'L': true,
308 'M': true,
309 'N': true,
310 'O': true,
311 'P': true,
312 'Q': true,
313 'R': true,
314 'S': true,
315 'T': true,
316 'U': true,
317 'V': true,
318 'W': true,
319 'X': true,
320 'Y': true,
321 'Z': true,
322 '[': true,
323 '\\': false,
324 ']': true,
325 '^': true,
326 '_': true,
327 '`': true,
328 'a': true,
329 'b': true,
330 'c': true,
331 'd': true,
332 'e': true,
333 'f': true,
334 'g': true,
335 'h': true,
336 'i': true,
337 'j': true,
338 'k': true,
339 'l': true,
340 'm': true,
341 'n': true,
342 'o': true,
343 'p': true,
344 'q': true,
345 'r': true,
346 's': true,
347 't': true,
348 'u': true,
349 'v': true,
350 'w': true,
351 'x': true,
352 'y': true,
353 'z': true,
354 '{': true,
355 '|': true,
356 '}': true,
357 '~': true,
358 '\u007f': true,
359 }
360
View as plain text