|
1 |
| -/* |
2 |
| -The conversion functions take a key and a map[string]string. First it checks if the key exists and logs and error |
3 |
| -if this is not the case. Second the conversion to the type is done. In case of an error and error is logged and the |
4 |
| -default values is returned. This guarantees that also if a field is missing or is not defined, still the full metricset |
5 |
| -is returned. |
6 |
| -*/ |
7 | 1 | package helper
|
8 | 2 |
|
9 | 3 | import (
|
| 4 | + "fmt" |
10 | 5 | "strconv"
|
11 | 6 |
|
| 7 | + "github.com/elastic/beats/libbeat/common" |
12 | 8 | "github.com/elastic/beats/libbeat/logp"
|
13 | 9 | )
|
14 | 10 |
|
| 11 | +// Schema describes how a map[string]string object can be parsed and converted into |
| 12 | +// an event. The conversions can be described using an (optionally nested) common.MapStr |
| 13 | +// that contains Conv objects. |
| 14 | +type Schema struct { |
| 15 | + conversions common.MapStr |
| 16 | +} |
| 17 | + |
| 18 | +// A Conv object represents a conversion mechanism from the data map to the event map. |
| 19 | +type Conv struct { |
| 20 | + Func Convertor // Convertor function |
| 21 | + Key string // The key in the data map |
| 22 | + Optional bool // Whether to log errors if the key is not found |
| 23 | +} |
| 24 | + |
| 25 | +// Convertor function type |
| 26 | +type Convertor func(key string, data map[string]string) (interface{}, error) |
| 27 | + |
| 28 | +// NewSchema creates a new converting schema. |
| 29 | +func NewSchema(conversions common.MapStr) Schema { |
| 30 | + return Schema{conversions} |
| 31 | +} |
| 32 | + |
| 33 | +// ApplyTo adds the fields extracted from data, converted using the schema, to the |
| 34 | +// event map. |
| 35 | +func (s Schema) ApplyTo(event common.MapStr, data map[string]string) common.MapStr { |
| 36 | + applySchemaToEvent(event, data, s.conversions) |
| 37 | + return event |
| 38 | +} |
| 39 | + |
| 40 | +// Apply converts the fields extracted from data, using the schema, into a new map. |
| 41 | +func (s Schema) Apply(data map[string]string) common.MapStr { |
| 42 | + return s.ApplyTo(common.MapStr{}, data) |
| 43 | +} |
| 44 | + |
| 45 | +func applySchemaToEvent(event common.MapStr, data map[string]string, conversions common.MapStr) { |
| 46 | + for key, conversion := range conversions { |
| 47 | + switch conversion.(type) { |
| 48 | + case Conv: |
| 49 | + conv := conversion.(Conv) |
| 50 | + value, err := conv.Func(conv.Key, data) |
| 51 | + if err != nil { |
| 52 | + if !conv.Optional { |
| 53 | + logp.Err("Error on field '%s': %v", key, err) |
| 54 | + } |
| 55 | + } else { |
| 56 | + event[key] = value |
| 57 | + } |
| 58 | + case common.MapStr: |
| 59 | + subEvent := common.MapStr{} |
| 60 | + applySchemaToEvent(subEvent, data, conversion.(common.MapStr)) |
| 61 | + event[key] = subEvent |
| 62 | + } |
| 63 | + } |
| 64 | +} |
| 65 | + |
15 | 66 | // ToBool converts value to bool. In case of error, returns false
|
16 |
| -func ToBool(key string, data map[string]string) bool { |
| 67 | +func ToBool(key string, data map[string]string) (interface{}, error) { |
17 | 68 |
|
18 | 69 | exists := checkExist(key, data)
|
19 | 70 | if !exists {
|
20 |
| - logp.Err("Key does not exist in in data: %s", key) |
21 |
| - return false |
| 71 | + return false, fmt.Errorf("Key `%s` not found", key) |
22 | 72 | }
|
23 | 73 |
|
24 | 74 | value, err := strconv.ParseBool(data[key])
|
25 | 75 | if err != nil {
|
26 |
| - logp.Err("Error converting param to bool: %s", key) |
27 |
| - return false |
| 76 | + return false, fmt.Errorf("Error converting param to bool: %s", key) |
28 | 77 | }
|
29 | 78 |
|
30 |
| - return value |
| 79 | + return value, nil |
| 80 | +} |
| 81 | + |
| 82 | +// Bool creates a Conv object for parsing booleans |
| 83 | +func Bool(key string, opts ...SchemaOption) Conv { |
| 84 | + return setOptions(Conv{Key: key, Func: ToBool}, opts) |
31 | 85 | }
|
32 | 86 |
|
33 | 87 | // ToFloat converts value to float64. In case of error, returns 0.0
|
34 |
| -func ToFloat(key string, data map[string]string) float64 { |
| 88 | +func ToFloat(key string, data map[string]string) (interface{}, error) { |
35 | 89 |
|
36 | 90 | exists := checkExist(key, data)
|
37 | 91 | if !exists {
|
38 |
| - logp.Err("Key does not exist in in data: %s", key) |
39 |
| - return 0.0 |
| 92 | + return false, fmt.Errorf("Key `%s` not found", key) |
40 | 93 | }
|
41 | 94 |
|
42 | 95 | value, err := strconv.ParseFloat(data[key], 64)
|
43 | 96 | if err != nil {
|
44 |
| - logp.Err("Error converting param to float: %s", key) |
45 |
| - value = 0.0 |
| 97 | + return 0.0, fmt.Errorf("Error converting param to float: %s", key) |
46 | 98 | }
|
47 | 99 |
|
48 |
| - return value |
| 100 | + return value, nil |
| 101 | +} |
| 102 | + |
| 103 | +// Float creates a Conv object for parsing floats |
| 104 | +func Float(key string, opts ...SchemaOption) Conv { |
| 105 | + return setOptions(Conv{Key: key, Func: ToFloat}, opts) |
49 | 106 | }
|
50 | 107 |
|
51 | 108 | // ToInt converts value to int. In case of error, returns 0
|
52 |
| -func ToInt(key string, data map[string]string) int64 { |
| 109 | +func ToInt(key string, data map[string]string) (interface{}, error) { |
53 | 110 |
|
54 | 111 | exists := checkExist(key, data)
|
55 | 112 | if !exists {
|
56 |
| - logp.Err("Key does not exist in in data: %s", key) |
57 |
| - return 0 |
| 113 | + return false, fmt.Errorf("Key `%s` not found", key) |
58 | 114 | }
|
59 | 115 |
|
60 | 116 | value, err := strconv.ParseInt(data[key], 10, 64)
|
61 | 117 | if err != nil {
|
62 |
| - logp.Err("Error converting param to int: %s", key) |
63 |
| - return 0 |
| 118 | + return 0, fmt.Errorf("Error converting param to int: %s", key) |
64 | 119 | }
|
65 | 120 |
|
66 |
| - return value |
| 121 | + return value, nil |
| 122 | +} |
| 123 | + |
| 124 | +// Int creates a Conv object for parsing integers |
| 125 | +func Int(key string, opts ...SchemaOption) Conv { |
| 126 | + return setOptions(Conv{Key: key, Func: ToInt}, opts) |
67 | 127 | }
|
68 | 128 |
|
69 | 129 | // ToStr converts value to str. In case of error, returns ""
|
70 |
| -func ToStr(key string, data map[string]string) string { |
| 130 | +func ToStr(key string, data map[string]string) (interface{}, error) { |
71 | 131 |
|
72 | 132 | exists := checkExist(key, data)
|
73 | 133 | if !exists {
|
74 |
| - logp.Err("Key does not exist in in data: %s", key) |
75 |
| - return "" |
| 134 | + return false, fmt.Errorf("Key `%s` not found", key) |
76 | 135 | }
|
77 | 136 |
|
78 |
| - return data[key] |
| 137 | + return data[key], nil |
| 138 | +} |
| 139 | + |
| 140 | +// Str creates a Conv object for parsing strings |
| 141 | +func Str(key string, opts ...SchemaOption) Conv { |
| 142 | + return setOptions(Conv{Key: key, Func: ToStr}, opts) |
79 | 143 | }
|
80 | 144 |
|
81 | 145 | // checkExists checks if a key exists in the given data set
|
82 | 146 | func checkExist(key string, data map[string]string) bool {
|
83 | 147 | _, ok := data[key]
|
84 | 148 | return ok
|
85 | 149 | }
|
| 150 | + |
| 151 | +// SchemaOption is for adding optional parameters to the conversion |
| 152 | +// functions |
| 153 | +type SchemaOption func(c Conv) Conv |
| 154 | + |
| 155 | +// The optional flag suppresses the error message in case the key |
| 156 | +// doesn't exist or results in an error. |
| 157 | +func Optional(c Conv) Conv { |
| 158 | + c.Optional = true |
| 159 | + return c |
| 160 | +} |
| 161 | + |
| 162 | +// setOptions adds the optional flags to the Conv object |
| 163 | +func setOptions(c Conv, opts []SchemaOption) Conv { |
| 164 | + for _, opt := range opts { |
| 165 | + c = opt(c) |
| 166 | + } |
| 167 | + return c |
| 168 | +} |
0 commit comments