Source file
src/log/slog/handler.go
1
2
3
4
5 package slog
6
7 import (
8 "context"
9 "fmt"
10 "io"
11 "log/slog/internal/buffer"
12 "reflect"
13 "slices"
14 "strconv"
15 "sync"
16 "time"
17 )
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 type Handler interface {
34
35
36
37
38
39
40
41
42
43 Enabled(context.Context, Level) bool
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 Handle(context.Context, Record) error
66
67
68
69
70 WithAttrs(attrs []Attr) Handler
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 WithGroup(name string) Handler
92 }
93
94 type defaultHandler struct {
95 ch *commonHandler
96
97 output func(pc uintptr, data []byte) error
98 }
99
100 func newDefaultHandler(output func(uintptr, []byte) error) *defaultHandler {
101 return &defaultHandler{
102 ch: &commonHandler{json: false},
103 output: output,
104 }
105 }
106
107 func (*defaultHandler) Enabled(_ context.Context, l Level) bool {
108 return l >= logLoggerLevel.Level()
109 }
110
111
112
113
114 func (h *defaultHandler) Handle(ctx context.Context, r Record) error {
115 buf := buffer.New()
116 buf.WriteString(r.Level.String())
117 buf.WriteByte(' ')
118 buf.WriteString(r.Message)
119 state := h.ch.newHandleState(buf, true, " ")
120 defer state.free()
121 state.appendNonBuiltIns(r)
122 return h.output(r.PC, *buf)
123 }
124
125 func (h *defaultHandler) WithAttrs(as []Attr) Handler {
126 return &defaultHandler{h.ch.withAttrs(as), h.output}
127 }
128
129 func (h *defaultHandler) WithGroup(name string) Handler {
130 return &defaultHandler{h.ch.withGroup(name), h.output}
131 }
132
133
134
135 type HandlerOptions struct {
136
137
138 AddSource bool
139
140
141
142
143
144
145 Level Leveler
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172 ReplaceAttr func(groups []string, a Attr) Attr
173 }
174
175
176 const (
177
178
179 TimeKey = "time"
180
181
182 LevelKey = "level"
183
184
185 MessageKey = "msg"
186
187
188 SourceKey = "source"
189 )
190
191 type commonHandler struct {
192 json bool
193 opts HandlerOptions
194 preformattedAttrs []byte
195
196
197
198
199 groupPrefix string
200 groups []string
201 nOpenGroups int
202 mu *sync.Mutex
203 w io.Writer
204 }
205
206 func (h *commonHandler) clone() *commonHandler {
207
208 return &commonHandler{
209 json: h.json,
210 opts: h.opts,
211 preformattedAttrs: slices.Clip(h.preformattedAttrs),
212 groupPrefix: h.groupPrefix,
213 groups: slices.Clip(h.groups),
214 nOpenGroups: h.nOpenGroups,
215 w: h.w,
216 mu: h.mu,
217 }
218 }
219
220
221
222 func (h *commonHandler) enabled(l Level) bool {
223 minLevel := LevelInfo
224 if h.opts.Level != nil {
225 minLevel = h.opts.Level.Level()
226 }
227 return l >= minLevel
228 }
229
230 func (h *commonHandler) withAttrs(as []Attr) *commonHandler {
231
232
233 if countEmptyGroups(as) == len(as) {
234 return h
235 }
236 h2 := h.clone()
237
238 state := h2.newHandleState((*buffer.Buffer)(&h2.preformattedAttrs), false, "")
239 defer state.free()
240 state.prefix.WriteString(h.groupPrefix)
241 if pfa := h2.preformattedAttrs; len(pfa) > 0 {
242 state.sep = h.attrSep()
243 if h2.json && pfa[len(pfa)-1] == '{' {
244 state.sep = ""
245 }
246 }
247
248 pos := state.buf.Len()
249 state.openGroups()
250 if !state.appendAttrs(as) {
251 state.buf.SetLen(pos)
252 } else {
253
254 h2.groupPrefix = state.prefix.String()
255
256
257 h2.nOpenGroups = len(h2.groups)
258 }
259 return h2
260 }
261
262 func (h *commonHandler) withGroup(name string) *commonHandler {
263 h2 := h.clone()
264 h2.groups = append(h2.groups, name)
265 return h2
266 }
267
268
269
270 func (h *commonHandler) handle(r Record) error {
271 state := h.newHandleState(buffer.New(), true, "")
272 defer state.free()
273 if h.json {
274 state.buf.WriteByte('{')
275 }
276
277 stateGroups := state.groups
278 state.groups = nil
279 rep := h.opts.ReplaceAttr
280
281 if !r.Time.IsZero() {
282 key := TimeKey
283 val := r.Time.Round(0)
284 if rep == nil {
285 state.appendKey(key)
286 state.appendTime(val)
287 } else {
288 state.appendAttr(Time(key, val))
289 }
290 }
291
292 key := LevelKey
293 val := r.Level
294 if rep == nil {
295 state.appendKey(key)
296 state.appendString(val.String())
297 } else {
298 state.appendAttr(Any(key, val))
299 }
300
301 if h.opts.AddSource {
302 state.appendAttr(Any(SourceKey, r.source()))
303 }
304 key = MessageKey
305 msg := r.Message
306 if rep == nil {
307 state.appendKey(key)
308 state.appendString(msg)
309 } else {
310 state.appendAttr(String(key, msg))
311 }
312 state.groups = stateGroups
313 state.appendNonBuiltIns(r)
314 state.buf.WriteByte('\n')
315
316 h.mu.Lock()
317 defer h.mu.Unlock()
318 _, err := h.w.Write(*state.buf)
319 return err
320 }
321
322 func (s *handleState) appendNonBuiltIns(r Record) {
323
324 if pfa := s.h.preformattedAttrs; len(pfa) > 0 {
325 s.buf.WriteString(s.sep)
326 s.buf.Write(pfa)
327 s.sep = s.h.attrSep()
328 if s.h.json && pfa[len(pfa)-1] == '{' {
329 s.sep = ""
330 }
331 }
332
333
334
335 nOpenGroups := s.h.nOpenGroups
336 if r.NumAttrs() > 0 {
337 s.prefix.WriteString(s.h.groupPrefix)
338
339
340
341
342 pos := s.buf.Len()
343 s.openGroups()
344 nOpenGroups = len(s.h.groups)
345 empty := true
346 r.Attrs(func(a Attr) bool {
347 if s.appendAttr(a) {
348 empty = false
349 }
350 return true
351 })
352 if empty {
353 s.buf.SetLen(pos)
354 nOpenGroups = s.h.nOpenGroups
355 }
356 }
357 if s.h.json {
358
359 for range s.h.groups[:nOpenGroups] {
360 s.buf.WriteByte('}')
361 }
362
363 s.buf.WriteByte('}')
364 }
365 }
366
367
368 func (h *commonHandler) attrSep() string {
369 if h.json {
370 return ","
371 }
372 return " "
373 }
374
375
376
377
378 type handleState struct {
379 h *commonHandler
380 buf *buffer.Buffer
381 freeBuf bool
382 sep string
383 prefix *buffer.Buffer
384 groups *[]string
385 }
386
387 var groupPool = sync.Pool{New: func() any {
388 s := make([]string, 0, 10)
389 return &s
390 }}
391
392 func (h *commonHandler) newHandleState(buf *buffer.Buffer, freeBuf bool, sep string) handleState {
393 s := handleState{
394 h: h,
395 buf: buf,
396 freeBuf: freeBuf,
397 sep: sep,
398 prefix: buffer.New(),
399 }
400 if h.opts.ReplaceAttr != nil {
401 s.groups = groupPool.Get().(*[]string)
402 *s.groups = append(*s.groups, h.groups[:h.nOpenGroups]...)
403 }
404 return s
405 }
406
407 func (s *handleState) free() {
408 if s.freeBuf {
409 s.buf.Free()
410 }
411 if gs := s.groups; gs != nil {
412 *gs = (*gs)[:0]
413 groupPool.Put(gs)
414 }
415 s.prefix.Free()
416 }
417
418 func (s *handleState) openGroups() {
419 for _, n := range s.h.groups[s.h.nOpenGroups:] {
420 s.openGroup(n)
421 }
422 }
423
424
425 const keyComponentSep = '.'
426
427
428
429 func (s *handleState) openGroup(name string) {
430 if s.h.json {
431 s.appendKey(name)
432 s.buf.WriteByte('{')
433 s.sep = ""
434 } else {
435 s.prefix.WriteString(name)
436 s.prefix.WriteByte(keyComponentSep)
437 }
438
439 if s.groups != nil {
440 *s.groups = append(*s.groups, name)
441 }
442 }
443
444
445 func (s *handleState) closeGroup(name string) {
446 if s.h.json {
447 s.buf.WriteByte('}')
448 } else {
449 (*s.prefix) = (*s.prefix)[:len(*s.prefix)-len(name)-1 ]
450 }
451 s.sep = s.h.attrSep()
452 if s.groups != nil {
453 *s.groups = (*s.groups)[:len(*s.groups)-1]
454 }
455 }
456
457
458
459 func (s *handleState) appendAttrs(as []Attr) bool {
460 nonEmpty := false
461 for _, a := range as {
462 if s.appendAttr(a) {
463 nonEmpty = true
464 }
465 }
466 return nonEmpty
467 }
468
469
470
471
472 func (s *handleState) appendAttr(a Attr) bool {
473 a.Value = a.Value.Resolve()
474 if rep := s.h.opts.ReplaceAttr; rep != nil && a.Value.Kind() != KindGroup {
475 var gs []string
476 if s.groups != nil {
477 gs = *s.groups
478 }
479
480 a = rep(gs, a)
481
482 a.Value = a.Value.Resolve()
483 }
484
485 if a.isEmpty() {
486 return false
487 }
488
489 if v := a.Value; v.Kind() == KindAny {
490 if src, ok := v.Any().(*Source); ok {
491 if s.h.json {
492 a.Value = src.group()
493 } else {
494 a.Value = StringValue(fmt.Sprintf("%s:%d", src.File, src.Line))
495 }
496 }
497 }
498 if a.Value.Kind() == KindGroup {
499 attrs := a.Value.Group()
500
501 if len(attrs) > 0 {
502
503
504
505
506 pos := s.buf.Len()
507
508 if a.Key != "" {
509 s.openGroup(a.Key)
510 }
511 if !s.appendAttrs(attrs) {
512 s.buf.SetLen(pos)
513 return false
514 }
515 if a.Key != "" {
516 s.closeGroup(a.Key)
517 }
518 }
519 } else {
520 s.appendKey(a.Key)
521 s.appendValue(a.Value)
522 }
523 return true
524 }
525
526 func (s *handleState) appendError(err error) {
527 s.appendString(fmt.Sprintf("!ERROR:%v", err))
528 }
529
530 func (s *handleState) appendKey(key string) {
531 s.buf.WriteString(s.sep)
532 if s.prefix != nil && len(*s.prefix) > 0 {
533 s.appendTwoStrings(string(*s.prefix), key)
534 } else {
535 s.appendString(key)
536 }
537 if s.h.json {
538 s.buf.WriteByte(':')
539 } else {
540 s.buf.WriteByte('=')
541 }
542 s.sep = s.h.attrSep()
543 }
544
545
546 func (s *handleState) appendTwoStrings(x, y string) {
547 buf := *s.buf
548 switch {
549 case s.h.json:
550 buf.WriteByte('"')
551 buf = appendEscapedJSONString(buf, x)
552 buf = appendEscapedJSONString(buf, y)
553 buf.WriteByte('"')
554 case !needsQuoting(x) && !needsQuoting(y):
555 buf.WriteString(x)
556 buf.WriteString(y)
557 default:
558 buf = strconv.AppendQuote(buf, x+y)
559 }
560 *s.buf = buf
561 }
562
563 func (s *handleState) appendString(str string) {
564 if s.h.json {
565 s.buf.WriteByte('"')
566 *s.buf = appendEscapedJSONString(*s.buf, str)
567 s.buf.WriteByte('"')
568 } else {
569
570 if needsQuoting(str) {
571 *s.buf = strconv.AppendQuote(*s.buf, str)
572 } else {
573 s.buf.WriteString(str)
574 }
575 }
576 }
577
578 func (s *handleState) appendValue(v Value) {
579 defer func() {
580 if r := recover(); r != nil {
581
582
583
584
585
586 if v := reflect.ValueOf(v.any); v.Kind() == reflect.Pointer && v.IsNil() {
587 s.appendString("<nil>")
588 return
589 }
590
591
592 s.appendString(fmt.Sprintf("!PANIC: %v", r))
593 }
594 }()
595
596 var err error
597 if s.h.json {
598 err = appendJSONValue(s, v)
599 } else {
600 err = appendTextValue(s, v)
601 }
602 if err != nil {
603 s.appendError(err)
604 }
605 }
606
607 func (s *handleState) appendTime(t time.Time) {
608 if s.h.json {
609 appendJSONTime(s, t)
610 } else {
611 *s.buf = appendRFC3339Millis(*s.buf, t)
612 }
613 }
614
615 func appendRFC3339Millis(b []byte, t time.Time) []byte {
616
617
618
619
620 const prefixLen = len("2006-01-02T15:04:05.000")
621 n := len(b)
622 t = t.Truncate(time.Millisecond).Add(time.Millisecond / 10)
623 b = t.AppendFormat(b, time.RFC3339Nano)
624 b = append(b[:n+prefixLen], b[n+prefixLen+1:]...)
625 return b
626 }
627
628
629
630 var DiscardHandler Handler = discardHandler{}
631
632 type discardHandler struct{}
633
634 func (dh discardHandler) Enabled(context.Context, Level) bool { return false }
635 func (dh discardHandler) Handle(context.Context, Record) error { return nil }
636 func (dh discardHandler) WithAttrs(attrs []Attr) Handler { return dh }
637 func (dh discardHandler) WithGroup(name string) Handler { return dh }
638
View as plain text