]> Cypherpunks.ru repositories - gorecfile.git/blob - slog/handler.go
0a2acd4e547b01cb52e39a8a80c33cc78a091831
[gorecfile.git] / slog / handler.go
1 package slog
2
3 import (
4         "bytes"
5         "context"
6         "io"
7         "log/slog"
8         "sync"
9         "time"
10
11         "go.cypherpunks.ru/recfile"
12 )
13
14 type RecfileHandler struct {
15         W        io.Writer
16         Level    slog.Level
17         LevelKey string
18         MsgKey   string
19         TimeKey  string
20         attrs    []slog.Attr
21         group    string
22         m        *sync.Mutex
23 }
24
25 func NewRecfileHandler(
26         w io.Writer,
27         level slog.Level,
28         levelKey, msgKey, timeKey string,
29 ) *RecfileHandler {
30         return &RecfileHandler{
31                 W:        w,
32                 Level:    level,
33                 LevelKey: levelKey,
34                 MsgKey:   msgKey,
35                 TimeKey:  timeKey,
36                 m:        new(sync.Mutex),
37         }
38 }
39
40 func (h *RecfileHandler) Enabled(ctx context.Context, level slog.Level) bool {
41         return level >= h.Level
42 }
43
44 func (h *RecfileHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
45         return &RecfileHandler{
46                 W:        h.W,
47                 Level:    h.Level,
48                 LevelKey: h.LevelKey,
49                 MsgKey:   h.MsgKey,
50                 TimeKey:  h.TimeKey,
51                 attrs:    append(h.attrs, attrs...),
52                 group:    h.group,
53                 m:        h.m,
54         }
55 }
56
57 func (h *RecfileHandler) WithGroup(name string) slog.Handler {
58         neu := RecfileHandler{
59                 W:        h.W,
60                 Level:    h.Level,
61                 LevelKey: h.LevelKey,
62                 MsgKey:   h.MsgKey,
63                 TimeKey:  h.TimeKey,
64                 attrs:    h.attrs,
65                 group:    h.group + name + "_",
66                 m:        h.m,
67         }
68         return &neu
69 }
70
71 func writeValue(w *recfile.Writer, group string, attr slog.Attr) (err error) {
72         if attr.Value.Kind() == slog.KindAny {
73                 multiline, ok := attr.Value.Any().([]string)
74                 if ok {
75                         if len(multiline) > 0 {
76                                 _, err = w.WriteFieldMultiline(group+attr.Key, multiline)
77                                 return
78                         }
79                         return
80                 }
81         }
82         _, err = w.WriteFields(recfile.Field{
83                 Name:  group + attr.Key,
84                 Value: attr.Value.String(),
85         })
86         return
87 }
88
89 func (h *RecfileHandler) Handle(ctx context.Context, rec slog.Record) (err error) {
90         var b bytes.Buffer
91         w := recfile.NewWriter(&b)
92         _, err = w.RecordStart()
93         if err != nil {
94                 panic(err)
95         }
96         var fields []recfile.Field
97         if h.LevelKey != "" {
98                 fields = append(fields, recfile.Field{
99                         Name:  h.LevelKey,
100                         Value: rec.Level.String(),
101                 })
102         }
103         if h.TimeKey != "" {
104                 fields = append(fields, recfile.Field{
105                         Name:  h.TimeKey,
106                         Value: rec.Time.UTC().Format(time.RFC3339Nano),
107                 })
108         }
109         fields = append(fields, recfile.Field{Name: h.MsgKey, Value: rec.Message})
110         _, err = w.WriteFields(fields...)
111         if err != nil {
112                 return
113         }
114         for _, attr := range h.attrs {
115                 writeValue(w, h.group, attr)
116         }
117         rec.Attrs(func(attr slog.Attr) bool { return writeValue(w, h.group, attr) == nil })
118         h.m.Lock()
119         n, err := h.W.Write(b.Bytes())
120         h.m.Unlock()
121         if err != nil {
122                 return
123         }
124         if n != b.Len() {
125                 return io.EOF
126         }
127         return
128 }