mirror of
https://codeberg.org/scip/yadu.git
synced 2025-12-16 20:21:00 +01:00
initial commit
This commit is contained in:
182
handler.go
Normal file
182
handler.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"log"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
const defaultTimeFormat = "2006-01-02T03:04.05 MST"
|
||||
|
||||
type YamlDumpHandler struct {
|
||||
l *log.Logger
|
||||
writer io.Writer
|
||||
mu *sync.Mutex
|
||||
level slog.Leveler
|
||||
groups []string
|
||||
attrs string
|
||||
timeFormat string
|
||||
replaceAttr func(groups []string, a slog.Attr) slog.Attr
|
||||
addSource bool
|
||||
indenter *regexp.Regexp
|
||||
}
|
||||
|
||||
type YamlDumpHandlerOptions struct {
|
||||
Level slog.Leveler
|
||||
ReplaceAttr func(groups []string, a slog.Attr) slog.Attr
|
||||
TimeFormat string
|
||||
AddSource bool
|
||||
}
|
||||
|
||||
func (h *YamlDumpHandler) Handle(ctx context.Context, r slog.Record) error {
|
||||
level := r.Level.String() + ":"
|
||||
|
||||
switch r.Level {
|
||||
case slog.LevelDebug:
|
||||
level = color.MagentaString(level)
|
||||
case slog.LevelInfo:
|
||||
level = color.BlueString(level)
|
||||
case slog.LevelWarn:
|
||||
level = color.YellowString(level)
|
||||
case slog.LevelError:
|
||||
level = color.RedString(level)
|
||||
}
|
||||
|
||||
fields := make(map[string]interface{}, r.NumAttrs())
|
||||
r.Attrs(func(a slog.Attr) bool {
|
||||
fields[a.Key] = a.Value.Any()
|
||||
return true
|
||||
})
|
||||
|
||||
tree := h.attrs
|
||||
|
||||
if len(fields) > 0 {
|
||||
bytetree, err := yaml.Marshal(&fields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tree = h.Postprocess(bytetree)
|
||||
}
|
||||
|
||||
timeStr := ""
|
||||
timeAttr := slog.Time(slog.TimeKey, r.Time)
|
||||
|
||||
if h.replaceAttr != nil {
|
||||
timeAttr = h.replaceAttr(nil, timeAttr)
|
||||
}
|
||||
|
||||
if !r.Time.IsZero() && !timeAttr.Equal(slog.Attr{}) {
|
||||
timeStr = r.Time.Format(h.timeFormat)
|
||||
}
|
||||
|
||||
msg := color.CyanString(r.Message)
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
if len(timeStr) > 0 {
|
||||
buf.WriteString(timeStr)
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
buf.WriteString(level)
|
||||
buf.WriteString(" ")
|
||||
buf.WriteString(msg)
|
||||
buf.WriteString(" ")
|
||||
buf.WriteString(color.WhiteString(tree))
|
||||
buf.WriteString("\n")
|
||||
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
_, err := h.writer.Write(buf.Bytes())
|
||||
|
||||
// h.l.Println(timeStr, level, msg, color.WhiteString(tree))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *YamlDumpHandler) Postprocess(yamlstr []byte) string {
|
||||
return "\n " + strings.TrimSpace(h.indenter.ReplaceAllString(string(yamlstr), " "))
|
||||
}
|
||||
|
||||
func NewYamlDumpHandler(out io.Writer, opts *YamlDumpHandlerOptions) *YamlDumpHandler {
|
||||
if opts == nil {
|
||||
opts = &YamlDumpHandlerOptions{}
|
||||
}
|
||||
|
||||
h := &YamlDumpHandler{
|
||||
writer: out,
|
||||
mu: &sync.Mutex{},
|
||||
level: opts.Level,
|
||||
timeFormat: opts.TimeFormat,
|
||||
replaceAttr: opts.ReplaceAttr,
|
||||
addSource: opts.AddSource,
|
||||
indenter: regexp.MustCompile(`(?m)^`),
|
||||
}
|
||||
|
||||
if h.timeFormat == "" {
|
||||
h.timeFormat = defaultTimeFormat
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// Enabled indicates whether the receiver logs at the given level.
|
||||
func (h *YamlDumpHandler) Enabled(_ context.Context, l slog.Level) bool {
|
||||
return l >= h.level.Level()
|
||||
}
|
||||
|
||||
// attributes plus attrs.
|
||||
func (h *YamlDumpHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
if len(attrs) == 0 {
|
||||
return h
|
||||
}
|
||||
|
||||
fields := make(map[string]interface{}, len(attrs))
|
||||
for _, a := range attrs {
|
||||
fields[a.Key] = a.Value.Any()
|
||||
}
|
||||
|
||||
bytetree, err := yaml.Marshal(&fields)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
h2 := h.clone()
|
||||
|
||||
h2.attrs += string(bytetree)
|
||||
return h2
|
||||
}
|
||||
|
||||
// WithGroup returns a new [log/slog.Handler] with name appended to the
|
||||
// receiver's groups.
|
||||
func (h *YamlDumpHandler) WithGroup(name string) slog.Handler {
|
||||
if name == "" {
|
||||
return h
|
||||
}
|
||||
h2 := h.clone()
|
||||
h2.groups = append(h2.groups, name)
|
||||
return h2
|
||||
}
|
||||
|
||||
func (h *YamlDumpHandler) clone() *YamlDumpHandler {
|
||||
return &YamlDumpHandler{
|
||||
writer: h.writer,
|
||||
mu: h.mu,
|
||||
level: h.level,
|
||||
groups: slices.Clip(h.groups),
|
||||
attrs: h.attrs,
|
||||
timeFormat: h.timeFormat,
|
||||
replaceAttr: h.replaceAttr,
|
||||
addSource: h.addSource,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user