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 func appendJSONMarshal(buf *buffer.Buffer, v any) error {
141
142 var bb bytes.Buffer
143 enc := json.NewEncoder(&bb)
144 enc.SetEscapeHTML(false)
145 if err := enc.Encode(v); err != nil {
146 return err
147 }
148 bs := bb.Bytes()
149 buf.Write(bs[:len(bs)-1])
150 return nil
151 }
152
153
154
155
156
157
158 func appendEscapedJSONString(buf []byte, s string) []byte {
159 char := func(b byte) { buf = append(buf, b) }
160 str := func(s string) { buf = append(buf, s...) }
161
162 start := 0
163 for i := 0; i < len(s); {
164 if b := s[i]; b < utf8.RuneSelf {
165 if safeSet[b] {
166 i++
167 continue
168 }
169 if start < i {
170 str(s[start:i])
171 }
172 char('\\')
173 switch b {
174 case '\\', '"':
175 char(b)
176 case '\n':
177 char('n')
178 case '\r':
179 char('r')
180 case '\t':
181 char('t')
182 default:
183
184 str(`u00`)
185 char(hex[b>>4])
186 char(hex[b&0xF])
187 }
188 i++
189 start = i
190 continue
191 }
192 c, size := utf8.DecodeRuneInString(s[i:])
193 if c == utf8.RuneError && size == 1 {
194 if start < i {
195 str(s[start:i])
196 }
197 str(`\ufffd`)
198 i += size
199 start = i
200 continue
201 }
202
203
204
205
206
207
208
209 if c == '\u2028' || c == '\u2029' {
210 if start < i {
211 str(s[start:i])
212 }
213 str(`\u202`)
214 char(hex[c&0xF])
215 i += size
216 start = i
217 continue
218 }
219 i += size
220 }
221 if start < len(s) {
222 str(s[start:])
223 }
224 return buf
225 }
226
227 const hex = "0123456789abcdef"
228
229
230
231
232
233
234
235
236
237 var safeSet = [utf8.RuneSelf]bool{
238 ' ': true,
239 '!': true,
240 '"': false,
241 '#': true,
242 '$': true,
243 '%': true,
244 '&': true,
245 '\'': true,
246 '(': true,
247 ')': true,
248 '*': true,
249 '+': true,
250 ',': true,
251 '-': true,
252 '.': true,
253 '/': true,
254 '0': true,
255 '1': true,
256 '2': true,
257 '3': true,
258 '4': true,
259 '5': true,
260 '6': true,
261 '7': true,
262 '8': true,
263 '9': true,
264 ':': true,
265 ';': true,
266 '<': true,
267 '=': true,
268 '>': true,
269 '?': true,
270 '@': true,
271 'A': true,
272 'B': true,
273 'C': true,
274 'D': true,
275 'E': true,
276 'F': true,
277 'G': true,
278 'H': true,
279 'I': true,
280 'J': true,
281 'K': true,
282 'L': true,
283 'M': true,
284 'N': true,
285 'O': true,
286 'P': true,
287 'Q': true,
288 'R': true,
289 'S': true,
290 'T': true,
291 'U': true,
292 'V': true,
293 'W': true,
294 'X': true,
295 'Y': true,
296 'Z': true,
297 '[': true,
298 '\\': false,
299 ']': true,
300 '^': true,
301 '_': true,
302 '`': true,
303 'a': true,
304 'b': true,
305 'c': true,
306 'd': true,
307 'e': true,
308 'f': true,
309 'g': true,
310 'h': true,
311 'i': true,
312 'j': true,
313 'k': true,
314 'l': true,
315 'm': true,
316 'n': true,
317 'o': true,
318 'p': true,
319 'q': true,
320 'r': true,
321 's': true,
322 't': true,
323 'u': true,
324 'v': true,
325 'w': true,
326 'x': true,
327 'y': true,
328 'z': true,
329 '{': true,
330 '|': true,
331 '}': true,
332 '~': true,
333 '\u007f': true,
334 }
335
View as plain text