]> Cypherpunks.ru repositories - gorecfile.git/blob - slog/handler.go
Unify copyright comment format
[gorecfile.git] / slog / handler.go
1 // recfile -- GNU recutils'es recfiles parser on pure Go
2 // Copyright (C) 2020-2024 Sergey Matveev <stargrave@stargrave.org>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, version 3 of the License.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 package slog
17
18 import (
19         "bytes"
20         "context"
21         "io"
22         "log/slog"
23         "sync"
24         "time"
25
26         "go.cypherpunks.ru/recfile"
27 )
28
29 type RecfileHandler struct {
30         W        io.Writer
31         Level    slog.Level
32         LevelKey string
33         MsgKey   string
34         TimeKey  string
35         attrs    []slog.Attr
36         group    string
37         m        *sync.Mutex
38 }
39
40 func NewRecfileHandler(
41         w io.Writer,
42         level slog.Level,
43         levelKey, msgKey, timeKey string,
44 ) *RecfileHandler {
45         return &RecfileHandler{
46                 W:        w,
47                 Level:    level,
48                 LevelKey: levelKey,
49                 MsgKey:   msgKey,
50                 TimeKey:  timeKey,
51                 m:        new(sync.Mutex),
52         }
53 }
54
55 func (h *RecfileHandler) Enabled(ctx context.Context, level slog.Level) bool {
56         return level >= h.Level
57 }
58
59 func (h *RecfileHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
60         return &RecfileHandler{
61                 W:        h.W,
62                 Level:    h.Level,
63                 LevelKey: h.LevelKey,
64                 MsgKey:   h.MsgKey,
65                 TimeKey:  h.TimeKey,
66                 attrs:    append(h.attrs, attrs...),
67                 group:    h.group,
68                 m:        h.m,
69         }
70 }
71
72 func (h *RecfileHandler) WithGroup(name string) slog.Handler {
73         neu := RecfileHandler{
74                 W:        h.W,
75                 Level:    h.Level,
76                 LevelKey: h.LevelKey,
77                 MsgKey:   h.MsgKey,
78                 TimeKey:  h.TimeKey,
79                 attrs:    h.attrs,
80                 group:    h.group + name + "_",
81                 m:        h.m,
82         }
83         return &neu
84 }
85
86 func writeValue(w *recfile.Writer, group string, attr slog.Attr) (err error) {
87         if attr.Value.Kind() == slog.KindAny {
88                 multiline, ok := attr.Value.Any().([]string)
89                 if ok {
90                         if len(multiline) > 0 {
91                                 _, err = w.WriteFieldMultiline(group+attr.Key, multiline)
92                                 return
93                         }
94                         return
95                 }
96         }
97         _, err = w.WriteFields(recfile.Field{
98                 Name:  group + attr.Key,
99                 Value: attr.Value.String(),
100         })
101         return
102 }
103
104 func (h *RecfileHandler) Handle(ctx context.Context, rec slog.Record) (err error) {
105         var b bytes.Buffer
106         w := recfile.NewWriter(&b)
107         _, err = w.RecordStart()
108         if err != nil {
109                 panic(err)
110         }
111         var fields []recfile.Field
112         if h.LevelKey != "" {
113                 fields = append(fields, recfile.Field{
114                         Name:  h.LevelKey,
115                         Value: rec.Level.String(),
116                 })
117         }
118         if h.TimeKey != "" {
119                 fields = append(fields, recfile.Field{
120                         Name:  h.TimeKey,
121                         Value: rec.Time.UTC().Format(time.RFC3339Nano),
122                 })
123         }
124         fields = append(fields, recfile.Field{Name: h.MsgKey, Value: rec.Message})
125         _, err = w.WriteFields(fields...)
126         if err != nil {
127                 return
128         }
129         for _, attr := range h.attrs {
130                 writeValue(w, h.group, attr)
131         }
132         rec.Attrs(func(attr slog.Attr) bool { return writeValue(w, h.group, attr) == nil })
133         h.m.Lock()
134         n, err := h.W.Write(b.Bytes())
135         h.m.Unlock()
136         if err != nil {
137                 return
138         }
139         if n != b.Len() {
140                 return io.EOF
141         }
142         return
143 }