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