-
Notifications
You must be signed in to change notification settings - Fork 52
/
logfmt_handler.go
104 lines (94 loc) · 2.21 KB
/
logfmt_handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package humanlog
import (
"bytes"
"time"
"github.com/go-logfmt/logfmt"
typesv1 "github.com/humanlogio/api/go/types/v1"
"google.golang.org/protobuf/types/known/timestamppb"
)
// LogfmtHandler can handle logs emmited by logrus.TextFormatter loggers.
type LogfmtHandler struct {
Opts *HandlerOptions
Level string
Time time.Time
Message string
Fields map[string]string
}
func (h *LogfmtHandler) clear() {
h.Level = ""
h.Time = time.Time{}
h.Message = ""
h.Fields = make(map[string]string)
}
// CanHandle tells if this line can be handled by this handler.
func (h *LogfmtHandler) TryHandle(d []byte, out *typesv1.StructuredLogEvent) bool {
if !bytes.ContainsRune(d, '=') {
return false
}
h.clear()
if !h.UnmarshalLogfmt(d) {
return false
}
out.Timestamp = timestamppb.New(h.Time)
out.Msg = h.Message
out.Lvl = h.Level
for k, v := range h.Fields {
out.Kvs = append(out.Kvs, &typesv1.KV{Key: k, Value: v})
}
return true
}
// HandleLogfmt sets the fields of the handler.
func (h *LogfmtHandler) UnmarshalLogfmt(data []byte) bool {
if h.Fields == nil {
h.Fields = make(map[string]string)
}
dec := logfmt.NewDecoder(bytes.NewReader(data))
for dec.ScanRecord() {
next_kv:
for dec.ScanKeyval() {
key := dec.Key()
val := dec.Value()
if h.Time.IsZero() {
foundTime := checkEachUntilFound(h.Opts.TimeFields, func(field string) bool {
if !bytes.Equal(key, []byte(field)) {
return false
}
time, ok := tryParseTime(string(val))
if ok {
h.Time = time
}
return ok
})
if foundTime {
continue next_kv
}
}
if len(h.Message) == 0 {
foundMessage := checkEachUntilFound(h.Opts.MessageFields, func(field string) bool {
if !bytes.Equal(key, []byte(field)) {
return false
}
h.Message = string(val)
return true
})
if foundMessage {
continue next_kv
}
}
if len(h.Level) == 0 {
foundLevel := checkEachUntilFound(h.Opts.LevelFields, func(field string) bool {
if !bytes.Equal(key, []byte(field)) {
return false
}
h.Level = string(val)
return true
})
if foundLevel {
continue next_kv
}
}
h.Fields[string(key)] = string(val)
}
}
return dec.Err() == nil
}