-
Notifications
You must be signed in to change notification settings - Fork 0
/
entries.go
194 lines (181 loc) · 4.58 KB
/
entries.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package entries
import (
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"
"time"
)
const (
StandardMessageField = "@message" // StandardMessageField is an unstructured logging payload
StandardTimestampField = "@timestamp" // StandardTimestampField represents the date and time that the LogEntry was emitted
StandardLevelField = "@level" // StandardLevelField specifies the logging level used
StandardModuleField = "@module" // StandardModuleField references a source system specific component hierarchy
StandardCallerField = "@caller" // StandardCallerField specifies the caller of the routine emitting this log entry
StandardTagField = "@tag" // StandardTagField contains classifiers for a LogEntry that may be unrelated to the payload, but relate to the context in which it was emitted
)
// LogEntry is a single entry in a log, with potentially many fields.
type LogEntry map[string]any
func (e LogEntry) HasField(name string) bool {
_, ok := e[name]
return ok
}
// Tag sets a tag on this LogEntry. A Tag is intended to classify the LogEntry in some way, presumably for filtering later.
// If a tag has already been set, then the parameter will be appended with a period separator.
func (e LogEntry) Tag(tag string) {
_tag, ok := e.AsString(StandardTagField)
if !ok {
e[StandardTagField] = tag
return
}
e[StandardTagField] = fmt.Sprintf("%s.%s", _tag, tag)
}
// HasTag determines if this LogEntry has a tag matching the parameter.
// Values will be compared ignoring case.
func (e LogEntry) HasTag(tag string) bool {
_tag, ok := e.AsString(StandardTagField)
if !ok {
return false
}
tags := strings.Split(_tag, ".")
for _, t := range tags {
if strings.ToLower(t) == strings.ToLower(tag) {
return true
}
}
return false
}
func (e LogEntry) AsFloat(name string) (float64, bool) {
if !e.HasField(name) {
return 0, false
}
if f, ok := e[name].(float64); ok {
return f, true
}
if s, ok := e[name].(string); ok {
f, err := strconv.ParseFloat(s, 64)
if err != nil {
return 0, false
}
return f, true
}
v := reflect.ValueOf(e[name])
if v.CanFloat() {
switch v.Kind() {
case reflect.Float64:
return e[name].(float64), true
case reflect.Float32:
return float64(e[name].(float32)), true
}
}
return 0, false
}
func (e LogEntry) AsInt(name string) (int64, bool) {
if !e.HasField(name) {
return 0, false
}
if i, ok := e[name].(int64); ok {
return i, true
}
if s, ok := e[name].(string); ok {
i, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return 0, false
}
return i, true
}
v := reflect.ValueOf(e[name])
if v.CanInt() {
switch v.Kind() {
case reflect.Int64:
return e[name].(int64), true
case reflect.Int32:
return int64(e[name].(int32)), true
case reflect.Int:
return int64(e[name].(int)), true
}
}
return 0, false
}
func (e LogEntry) AsUint(name string) (uint64, bool) {
if !e.HasField(name) {
return 0, false
}
if i, ok := e[name].(uint64); ok {
return i, true
}
if s, ok := e[name].(string); ok {
i, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return 0, false
}
return i, true
}
v := reflect.ValueOf(e[name])
if v.CanUint() {
switch v.Kind() {
case reflect.Uint64:
return e[name].(uint64), true
case reflect.Uint32:
return uint64(e[name].(uint32)), true
case reflect.Uint:
return uint64(e[name].(uint)), true
}
}
return 0, false
}
func (e LogEntry) AsString(name string) (string, bool) {
if !e.HasField(name) {
return "", false
}
if s, ok := e[name].(string); ok {
return s, true
}
if s, ok := e[name].(interface{ String() string }); ok {
return s.String(), true
}
if err, ok := e[name].(error); ok {
return err.Error(), true
}
return fmt.Sprintf("%v", e[name]), true
}
func (e LogEntry) AsTime(name string, format ...string) (time.Time, bool) {
var none time.Time
if !e.HasField(name) {
return none, false
}
if t, ok := e[name].(time.Time); ok {
return t.UTC(), true
}
if s, ok := e.AsString(name); ok {
if len(format) > 0 {
for _, f := range format {
t, err := time.Parse(f, s)
if err == nil {
return t.UTC(), true
}
}
} else {
t, err := time.Parse(time.RFC3339, s)
if err == nil {
return t.UTC(), true
}
}
}
return none, false
}
func (e LogEntry) Format(format string, fields ...string) string {
args := make([]any, len(fields))
for i, f := range fields {
args[i] = e[f]
}
return fmt.Sprintf(format, args...)
}
func FromString(msg string) LogEntry {
entry := LogEntry{}
if err := json.Unmarshal([]byte(msg), &entry); err != nil {
entry[StandardMessageField] = msg
}
return entry
}