diamond-orm/internal/logging/custom_handler_state.go

146 lines
2.6 KiB
Go

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
}}