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