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 src := r.Source()
303 if src == nil {
304 src = &Source{}
305 }
306 state.appendAttr(Any(SourceKey, src))
307 }
308 key = MessageKey
309 msg := r.Message
310 if rep == nil {
311 state.appendKey(key)
312 state.appendString(msg)
313 } else {
314 state.appendAttr(String(key, msg))
315 }
316 state.groups = stateGroups
317 state.appendNonBuiltIns(r)
318 state.buf.WriteByte('\n')
319
320 h.mu.Lock()
321 defer h.mu.Unlock()
322 _, err := h.w.Write(*state.buf)
323 return err
324 }
325
326 func (s *handleState) appendNonBuiltIns(r Record) {
327
328 if pfa := s.h.preformattedAttrs; len(pfa) > 0 {
329 s.buf.WriteString(s.sep)
330 s.buf.Write(pfa)
331 s.sep = s.h.attrSep()
332 if s.h.json && pfa[len(pfa)-1] == '{' {
333 s.sep = ""
334 }
335 }
336
337
338
339 nOpenGroups := s.h.nOpenGroups
340 if r.NumAttrs() > 0 {
341 s.prefix.WriteString(s.h.groupPrefix)
342
343
344
345
346 pos := s.buf.Len()
347 s.openGroups()
348 nOpenGroups = len(s.h.groups)
349 empty := true
350 r.Attrs(func(a Attr) bool {
351 if s.appendAttr(a) {
352 empty = false
353 }
354 return true
355 })
356 if empty {
357 s.buf.SetLen(pos)
358 nOpenGroups = s.h.nOpenGroups
359 }
360 }
361 if s.h.json {
362
363 for range s.h.groups[:nOpenGroups] {
364 s.buf.WriteByte('}')
365 }
366
367 s.buf.WriteByte('}')
368 }
369 }
370
371
372 func (h *commonHandler) attrSep() string {
373 if h.json {
374 return ","
375 }
376 return " "
377 }
378
379
380
381
382 type handleState struct {
383 h *commonHandler
384 buf *buffer.Buffer
385 freeBuf bool
386 sep string
387 prefix *buffer.Buffer
388 groups *[]string
389 }
390
391 var groupPool = sync.Pool{New: func() any {
392 s := make([]string, 0, 10)
393 return &s
394 }}
395
396 func (h *commonHandler) newHandleState(buf *buffer.Buffer, freeBuf bool, sep string) handleState {
397 s := handleState{
398 h: h,
399 buf: buf,
400 freeBuf: freeBuf,
401 sep: sep,
402 prefix: buffer.New(),
403 }
404 if h.opts.ReplaceAttr != nil {
405 s.groups = groupPool.Get().(*[]string)
406 *s.groups = append(*s.groups, h.groups[:h.nOpenGroups]...)
407 }
408 return s
409 }
410
411 func (s *handleState) free() {
412 if s.freeBuf {
413 s.buf.Free()
414 }
415 if gs := s.groups; gs != nil {
416 *gs = (*gs)[:0]
417 groupPool.Put(gs)
418 }
419 s.prefix.Free()
420 }
421
422 func (s *handleState) openGroups() {
423 for _, n := range s.h.groups[s.h.nOpenGroups:] {
424 s.openGroup(n)
425 }
426 }
427
428
429 const keyComponentSep = '.'
430
431
432
433 func (s *handleState) openGroup(name string) {
434 if s.h.json {
435 s.appendKey(name)
436 s.buf.WriteByte('{')
437 s.sep = ""
438 } else {
439 s.prefix.WriteString(name)
440 s.prefix.WriteByte(keyComponentSep)
441 }
442
443 if s.groups != nil {
444 *s.groups = append(*s.groups, name)
445 }
446 }
447
448
449 func (s *handleState) closeGroup(name string) {
450 if s.h.json {
451 s.buf.WriteByte('}')
452 } else {
453 (*s.prefix) = (*s.prefix)[:len(*s.prefix)-len(name)-1 ]
454 }
455 s.sep = s.h.attrSep()
456 if s.groups != nil {
457 *s.groups = (*s.groups)[:len(*s.groups)-1]
458 }
459 }
460
461
462
463 func (s *handleState) appendAttrs(as []Attr) bool {
464 nonEmpty := false
465 for _, a := range as {
466 if s.appendAttr(a) {
467 nonEmpty = true
468 }
469 }
470 return nonEmpty
471 }
472
473
474
475
476 func (s *handleState) appendAttr(a Attr) bool {
477 a.Value = a.Value.Resolve()
478 if rep := s.h.opts.ReplaceAttr; rep != nil && a.Value.Kind() != KindGroup {
479 var gs []string
480 if s.groups != nil {
481 gs = *s.groups
482 }
483
484 a = rep(gs, a)
485
486 a.Value = a.Value.Resolve()
487 }
488
489 if a.isEmpty() {
490 return false
491 }
492
493 if v := a.Value; v.Kind() == KindAny {
494 if src, ok := v.Any().(*Source); ok {
495 if s.h.json {
496 a.Value = src.group()
497 } else {
498 a.Value = StringValue(fmt.Sprintf("%s:%d", src.File, src.Line))
499 }
500 }
501 }
502 if a.Value.Kind() == KindGroup {
503 attrs := a.Value.Group()
504
505 if len(attrs) > 0 {
506
507
508
509
510 pos := s.buf.Len()
511
512 if a.Key != "" {
513 s.openGroup(a.Key)
514 }
515 if !s.appendAttrs(attrs) {
516 s.buf.SetLen(pos)
517 return false
518 }
519 if a.Key != "" {
520 s.closeGroup(a.Key)
521 }
522 }
523 } else {
524 s.appendKey(a.Key)
525 s.appendValue(a.Value)
526 }
527 return true
528 }
529
530 func (s *handleState) appendError(err error) {
531 s.appendString(fmt.Sprintf("!ERROR:%v", err))
532 }
533
534 func (s *handleState) appendKey(key string) {
535 s.buf.WriteString(s.sep)
536 if s.prefix != nil && len(*s.prefix) > 0 {
537 s.appendTwoStrings(string(*s.prefix), key)
538 } else {
539 s.appendString(key)
540 }
541 if s.h.json {
542 s.buf.WriteByte(':')
543 } else {
544 s.buf.WriteByte('=')
545 }
546 s.sep = s.h.attrSep()
547 }
548
549
550 func (s *handleState) appendTwoStrings(x, y string) {
551 buf := *s.buf
552 switch {
553 case s.h.json:
554 buf.WriteByte('"')
555 buf = appendEscapedJSONString(buf, x)
556 buf = appendEscapedJSONString(buf, y)
557 buf.WriteByte('"')
558 case !needsQuoting(x) && !needsQuoting(y):
559 buf.WriteString(x)
560 buf.WriteString(y)
561 default:
562 buf = strconv.AppendQuote(buf, x+y)
563 }
564 *s.buf = buf
565 }
566
567 func (s *handleState) appendString(str string) {
568 if s.h.json {
569 s.buf.WriteByte('"')
570 *s.buf = appendEscapedJSONString(*s.buf, str)
571 s.buf.WriteByte('"')
572 } else {
573
574 if needsQuoting(str) {
575 *s.buf = strconv.AppendQuote(*s.buf, str)
576 } else {
577 s.buf.WriteString(str)
578 }
579 }
580 }
581
582 func (s *handleState) appendValue(v Value) {
583 defer func() {
584 if r := recover(); r != nil {
585
586
587
588
589
590 if v := reflect.ValueOf(v.any); v.Kind() == reflect.Pointer && v.IsNil() {
591 s.appendString("<nil>")
592 return
593 }
594
595
596 s.appendString(fmt.Sprintf("!PANIC: %v", r))
597 }
598 }()
599
600 var err error
601 if s.h.json {
602 err = appendJSONValue(s, v)
603 } else {
604 err = appendTextValue(s, v)
605 }
606 if err != nil {
607 s.appendError(err)
608 }
609 }
610
611 func (s *handleState) appendTime(t time.Time) {
612 if s.h.json {
613 appendJSONTime(s, t)
614 } else {
615 *s.buf = appendRFC3339Millis(*s.buf, t)
616 }
617 }
618
619 func appendRFC3339Millis(b []byte, t time.Time) []byte {
620
621
622
623
624 const prefixLen = len("2006-01-02T15:04:05.000")
625 n := len(b)
626 t = t.Truncate(time.Millisecond).Add(time.Millisecond / 10)
627 b = t.AppendFormat(b, time.RFC3339Nano)
628 b = append(b[:n+prefixLen], b[n+prefixLen+1:]...)
629 return b
630 }
631
632
633
634 var DiscardHandler Handler = discardHandler{}
635
636 type discardHandler struct{}
637
638 func (dh discardHandler) Enabled(context.Context, Level) bool { return false }
639 func (dh discardHandler) Handle(context.Context, Record) error { return nil }
640 func (dh discardHandler) WithAttrs(attrs []Attr) Handler { return dh }
641 func (dh discardHandler) WithGroup(name string) Handler { return dh }
642
View as plain text