package logging import ( "bytes" "fmt" "log/slog" "strings" "sync" "time" ) func (f *FormattedHandler) newState(sb *bytes.Buffer) state { s := state{ fh: f, buf: sb, } if f.opts.ReplaceAttr != nil { s.groups = groupPool.Get().(*[]string) *s.groups = append(*s.groups, f.groups[:f.groupLvl]...) } return s } type state struct { buf *bytes.Buffer fh *FormattedHandler groups *[]string } func (s *state) startGroups() { for _, n := range s.fh.groups[s.fh.groupLvl:] { s.startGroup(n) } } func (s *state) startGroup(name string) { s.buf.WriteByte('\n') if s.groups != nil { *s.groups = append(*s.groups, name) } } func (s *state) endGroup() { if s.groups != nil { *s.groups = (*s.groups)[:len(*s.groups)-1] } } func (s *state) appendAttr(a slog.Attr) bool { a.Value = a.Value.Resolve() if rep := s.fh.opts.ReplaceAttr; rep != nil && a.Value.Kind() != slog.KindGroup { var gs []string if s.groups != nil { gs = *s.groups } a = rep(gs, a) a.Value = a.Value.Resolve() } if a.Equal(slog.Attr{}) || hasBuiltInKey(a) || a.Key == slog.LevelKey { return false } if a.Value.Kind() == slog.KindGroup { pos := s.buf.Len() attrs := a.Value.Group() if len(attrs) > 0 { if a.Key != "" { s.startGroup(a.Key) } if !s.appendAttrs(attrs) { s.buf.Truncate(pos) return false } if a.Key != "" { s.endGroup() } } } else { s.writeAttr(a) } return true } func (s *state) appendAttrs(as []slog.Attr) bool { nonEmpty := false for _, a := range as { if s.appendAttr(a) { nonEmpty = true } } return nonEmpty } func (s *state) writeAttr(a slog.Attr) { if s.buf.Len() > 0 { s.buf.WriteString(";") } if len(*s.groups) > 0 { s.buf.WriteString(fmt.Sprintf("%*s", len(*s.groups)*2, "")) s.buf.WriteString(strings.Join(*s.groups, ".")) s.buf.WriteString(".") } s.buf.WriteString(a.Key) s.buf.WriteString("=") switch a.Value.Kind() { case slog.KindDuration: s.buf.WriteString(a.Value.Duration().String()) case slog.KindTime: s.buf.WriteString(a.Value.Time().Format(time.RFC3339Nano)) default: s.buf.WriteString(fmt.Sprintf("%+v", a.Value.Any())) } } func (s *state) begin(r slog.Record) { if r.NumAttrs() > 0 { pos := s.buf.Len() s.startGroups() empty := true r.Attrs(func(a slog.Attr) bool { isBuiltIn := hasBuiltInKey(a) || a.Key == slog.LevelKey if !isBuiltIn && s.appendAttr(a) { empty = false } return true }) if empty { s.buf.Truncate(pos) } } } func (s *state) free() { if gs := s.groups; gs != nil { *gs = (*gs)[:0] groupPool.Put(gs) } s.buf.Reset() } var groupPool = sync.Pool{New: func() any { s := make([]string, 0, 10) return &s }}