forked from trivago/grok
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcompiledgrok.go
195 lines (160 loc) · 5.26 KB
/
compiledgrok.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
195
package grok
import (
"fmt"
"regexp"
"strconv"
)
// CompiledGrok represents a compiled Grok expression.
// Use Grok.Compile to generate a CompiledGrok object.
type CompiledGrok struct {
regexp *regexp.Regexp
typeHints typeHintByKey
removeEmpty bool
}
type typeHintByKey map[string]string
// GetFields returns a list of all named fields in this grok expression
func (compiled CompiledGrok) GetFields() []string {
return compiled.regexp.SubexpNames()
}
// Match returns true if the given data matches the pattern.
func (compiled CompiledGrok) Match(data []byte) bool {
return compiled.regexp.Match(data)
}
// MatchString returns true if the given text matches the pattern.
func (compiled CompiledGrok) MatchString(text string) bool {
return compiled.regexp.MatchString(text)
}
// Parse processes the given data and returns a map containing the values of
// all named fields as byte arrays. If a field is parsed more than once, the
// last match is return.
func (compiled CompiledGrok) Parse(data []byte) map[string][]byte {
captures := make(map[string][]byte)
if matches := compiled.regexp.FindSubmatch(data); len(matches) > 0 {
for idx, key := range compiled.GetFields() {
match := matches[idx]
if compiled.omitField(key, match) {
continue
}
captures[key] = match
}
}
return captures
}
// ParseString processes the given text and returns a map containing the values of
// all named fields as strings. If a field is parsed more than once, the
// last match is return.
func (compiled CompiledGrok) ParseString(text string) map[string]string {
captures := make(map[string]string)
if matches := compiled.regexp.FindStringSubmatch(text); len(matches) > 0 {
for idx, key := range compiled.GetFields() {
match := matches[idx]
if compiled.omitStringField(key, match) {
continue
}
captures[key] = match
}
}
return captures
}
// ParseTyped processes the given data and returns a map containing the values
// of all named fields converted to their corresponding types. If no typehint is
// given, the value will be converted to string.
func (compiled CompiledGrok) ParseTyped(data []byte) (map[string]interface{}, error) {
captures := make(map[string]interface{})
if matches := compiled.regexp.FindSubmatch(data); len(matches) > 0 {
for idx, key := range compiled.GetFields() {
match := matches[idx]
if compiled.omitField(key, match) {
continue
}
if val, err := compiled.typeCast(string(match), key); err == nil {
captures[key] = val
} else {
return nil, err
}
}
}
return captures, nil
}
// ParseStringTyped processes the given data and returns a map containing the
// values of all named fields converted to their corresponding types. If no
// typehint is given, the value will be converted to string.
func (compiled CompiledGrok) ParseStringTyped(text string) (map[string]interface{}, error) {
captures := make(map[string]interface{})
if matches := compiled.regexp.FindStringSubmatch(text); len(matches) > 0 {
for idx, key := range compiled.GetFields() {
match := matches[idx]
if compiled.omitStringField(key, match) {
continue
}
if val, err := compiled.typeCast(match, key); err == nil {
captures[key] = val
} else {
return nil, err
}
}
}
return captures, nil
}
// ParseToMultiMap acts like Parse but allows multiple matches per field.
func (compiled CompiledGrok) ParseToMultiMap(data []byte) map[string][][]byte {
captures := make(map[string][][]byte)
if matches := compiled.regexp.FindSubmatch(data); len(matches) > 0 {
for idx, key := range compiled.GetFields() {
match := matches[idx]
if compiled.omitField(key, match) {
continue
}
if values, exists := captures[key]; exists {
captures[key] = append(values, match)
} else {
captures[key] = [][]byte{match}
}
}
}
return captures
}
// ParseStringToMultiMap acts like ParseString but allows multiple matches per
// field.
func (compiled CompiledGrok) ParseStringToMultiMap(text string) map[string][]string {
captures := make(map[string][]string)
if matches := compiled.regexp.FindStringSubmatch(text); len(matches) > 0 {
for idx, key := range compiled.GetFields() {
match := matches[idx]
if compiled.omitStringField(key, match) {
continue
}
if values, exists := captures[key]; exists {
captures[key] = append(values, match)
} else {
captures[key] = []string{match}
}
}
}
return captures
}
// omitField return true if the field is to be omitted
func (compiled CompiledGrok) omitField(key string, match []byte) bool {
return len(key) == 0 || compiled.removeEmpty && len(match) == 0
}
// omitStringField return true if the field is to be omitted
func (compiled CompiledGrok) omitStringField(key, match string) bool {
return len(key) == 0 || compiled.removeEmpty && len(match) == 0
}
// typeCast casts a field based on a typehint
func (compiled CompiledGrok) typeCast(match, key string) (interface{}, error) {
typeName, hasTypeHint := compiled.typeHints[key]
if !hasTypeHint {
return match, nil
}
switch typeName {
case "int":
return strconv.Atoi(match)
case "float":
return strconv.ParseFloat(match, 64)
case "string":
return match, nil
default:
return nil, fmt.Errorf("ERROR the value %s cannot be converted to %s. Must be int, float, string or empty", match, typeName)
}
}