]> Cypherpunks.ru repositories - gorecfile.git/blobdiff - slog/handler.go
log.slog handler
[gorecfile.git] / slog / handler.go
diff --git a/slog/handler.go b/slog/handler.go
new file mode 100644 (file)
index 0000000..0a2acd4
--- /dev/null
@@ -0,0 +1,128 @@
+package slog
+
+import (
+       "bytes"
+       "context"
+       "io"
+       "log/slog"
+       "sync"
+       "time"
+
+       "go.cypherpunks.ru/recfile"
+)
+
+type RecfileHandler struct {
+       W        io.Writer
+       Level    slog.Level
+       LevelKey string
+       MsgKey   string
+       TimeKey  string
+       attrs    []slog.Attr
+       group    string
+       m        *sync.Mutex
+}
+
+func NewRecfileHandler(
+       w io.Writer,
+       level slog.Level,
+       levelKey, msgKey, timeKey string,
+) *RecfileHandler {
+       return &RecfileHandler{
+               W:        w,
+               Level:    level,
+               LevelKey: levelKey,
+               MsgKey:   msgKey,
+               TimeKey:  timeKey,
+               m:        new(sync.Mutex),
+       }
+}
+
+func (h *RecfileHandler) Enabled(ctx context.Context, level slog.Level) bool {
+       return level >= h.Level
+}
+
+func (h *RecfileHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
+       return &RecfileHandler{
+               W:        h.W,
+               Level:    h.Level,
+               LevelKey: h.LevelKey,
+               MsgKey:   h.MsgKey,
+               TimeKey:  h.TimeKey,
+               attrs:    append(h.attrs, attrs...),
+               group:    h.group,
+               m:        h.m,
+       }
+}
+
+func (h *RecfileHandler) WithGroup(name string) slog.Handler {
+       neu := RecfileHandler{
+               W:        h.W,
+               Level:    h.Level,
+               LevelKey: h.LevelKey,
+               MsgKey:   h.MsgKey,
+               TimeKey:  h.TimeKey,
+               attrs:    h.attrs,
+               group:    h.group + name + "_",
+               m:        h.m,
+       }
+       return &neu
+}
+
+func writeValue(w *recfile.Writer, group string, attr slog.Attr) (err error) {
+       if attr.Value.Kind() == slog.KindAny {
+               multiline, ok := attr.Value.Any().([]string)
+               if ok {
+                       if len(multiline) > 0 {
+                               _, err = w.WriteFieldMultiline(group+attr.Key, multiline)
+                               return
+                       }
+                       return
+               }
+       }
+       _, err = w.WriteFields(recfile.Field{
+               Name:  group + attr.Key,
+               Value: attr.Value.String(),
+       })
+       return
+}
+
+func (h *RecfileHandler) Handle(ctx context.Context, rec slog.Record) (err error) {
+       var b bytes.Buffer
+       w := recfile.NewWriter(&b)
+       _, err = w.RecordStart()
+       if err != nil {
+               panic(err)
+       }
+       var fields []recfile.Field
+       if h.LevelKey != "" {
+               fields = append(fields, recfile.Field{
+                       Name:  h.LevelKey,
+                       Value: rec.Level.String(),
+               })
+       }
+       if h.TimeKey != "" {
+               fields = append(fields, recfile.Field{
+                       Name:  h.TimeKey,
+                       Value: rec.Time.UTC().Format(time.RFC3339Nano),
+               })
+       }
+       fields = append(fields, recfile.Field{Name: h.MsgKey, Value: rec.Message})
+       _, err = w.WriteFields(fields...)
+       if err != nil {
+               return
+       }
+       for _, attr := range h.attrs {
+               writeValue(w, h.group, attr)
+       }
+       rec.Attrs(func(attr slog.Attr) bool { return writeValue(w, h.group, attr) == nil })
+       h.m.Lock()
+       n, err := h.W.Write(b.Bytes())
+       h.m.Unlock()
+       if err != nil {
+               return
+       }
+       if n != b.Len() {
+               return io.EOF
+       }
+       return
+}