This repository has been archived by the owner on Aug 18, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 31
/
configurableregexdeconstructor.go
109 lines (95 loc) · 3.43 KB
/
configurableregexdeconstructor.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
package metricdeconstructor
import (
"encoding/json"
"fmt"
"github.com/signalfx/golib/datapoint"
"regexp"
"sort"
"strings"
)
type configurableRegexMetricRule struct {
typer
Regex string `json:"Regex"`
MetricName string `json:"MetricName"`
AdditionalDimensions map[string]string `json:"AdditionalDimensions"`
MetricTypeString string `json:"MetricType"`
MetricType datapoint.MetricType
Compiled *regexp.Regexp
UseCount int64
}
type configurableRegexMetricDeconstructor struct {
MetricRules []*configurableRegexMetricRule `json:"MetricRules"`
FallBackDeconstructorName string `json:"FallbackDeconstructor"`
FallBackDeconstructorConfig string `json:"FallbackDeconstructorConfig"`
AdditionalDimensions map[string]string `json:"Dimensions"`
FallBackDeconstructor MetricDeconstructor
}
func regexJSONLoader(config map[string]interface{}) (MetricDeconstructor, error) {
var metrics configurableRegexMetricDeconstructor
// a wee bit of a hack, but at least we know it's valid json and it makes the config easier
jsonString, err := json.Marshal(config)
if err != nil {
return nil, err
}
err = json.Unmarshal(jsonString, &metrics)
if err != nil {
return nil, err
}
for _, metricRegex := range metrics.MetricRules {
if metricRegex.MetricTypeString != "" {
metricType, ok := nameToType[metricRegex.MetricTypeString]
if !ok {
return nil, fmt.Errorf("cannot parse configured MetricType of %s", metricRegex.MetricTypeString)
}
metricRegex.MetricType = metricType
}
metricRegex.Compiled, err = regexp.Compile(metricRegex.Regex)
if err != nil {
return nil, err
}
}
loader, exists := knownLoaders[metrics.FallBackDeconstructorName]
if !exists {
return nil, fmt.Errorf("unable to load metric deconstructor by the name of %s", metrics.FallBackDeconstructorName)
}
decon, err := loader(metrics.FallBackDeconstructorConfig)
if err != nil {
return nil, err
}
metrics.FallBackDeconstructor = decon
return &metrics, nil
}
// Parse turns the line into its dimensions, metric type and metric name based on configuration rules defined when constructing the deconstructor
func (m *configurableRegexMetricDeconstructor) Parse(originalMetric string) (string, datapoint.MetricType, map[string]string, error) {
for _, metric := range m.MetricRules {
if metric.Compiled.MatchString(originalMetric) {
ms := metric.Compiled.FindStringSubmatch(originalMetric)
nms := metric.Compiled.SubexpNames()
dimensions := map[string]string{}
metricNamePieces := []string{}
metricNameLookup := map[string]string{}
for i := 1; i < len(ms); i++ {
if strings.HasPrefix(nms[i], "sf_metric") {
metricNamePieces = append(metricNamePieces, nms[i])
metricNameLookup[nms[i]] = ms[i]
} else {
dimensions[nms[i]] = ms[i]
}
}
for k, v := range metric.AdditionalDimensions {
dimensions[k] = v
}
sort.Strings(metricNamePieces)
metricName := []string{metric.MetricName}
for _, piece := range metricNamePieces {
metricName = append(metricName, metricNameLookup[piece])
}
actualMetricName := strings.Join(metricName, "")
if actualMetricName == "" {
actualMetricName = originalMetric
}
return actualMetricName, metric.MetricType, dimensions, nil
}
}
return m.FallBackDeconstructor.Parse(originalMetric)
}