-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
Copy pathregex.go
136 lines (115 loc) · 3.26 KB
/
regex.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
package stages
import (
"reflect"
"regexp"
"time"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
)
// Config Errors
const (
ErrExpressionRequired = "expression is required"
ErrCouldNotCompileRegex = "could not compile regular expression"
ErrEmptyRegexStageConfig = "empty regex stage configuration"
ErrEmptyRegexStageSource = "empty source"
)
// RegexConfig contains a regexStage configuration
type RegexConfig struct {
Expression string `mapstructure:"expression"`
Source *string `mapstructure:"source"`
}
// validateRegexConfig validates the config and return a regex
func validateRegexConfig(c *RegexConfig) (*regexp.Regexp, error) {
if c == nil {
return nil, errors.New(ErrEmptyRegexStageConfig)
}
if c.Expression == "" {
return nil, errors.New(ErrExpressionRequired)
}
if c.Source != nil && *c.Source == "" {
return nil, errors.New(ErrEmptyRegexStageSource)
}
expr, err := regexp.Compile(c.Expression)
if err != nil {
return nil, errors.Wrap(err, ErrCouldNotCompileRegex)
}
return expr, nil
}
// regexStage sets extracted data using regular expressions
type regexStage struct {
cfg *RegexConfig
expression *regexp.Regexp
logger log.Logger
}
// newRegexStage creates a newRegexStage
func newRegexStage(logger log.Logger, config interface{}) (Stage, error) {
cfg, err := parseRegexConfig(config)
if err != nil {
return nil, err
}
expression, err := validateRegexConfig(cfg)
if err != nil {
return nil, err
}
return ®exStage{
cfg: cfg,
expression: expression,
logger: log.With(logger, "component", "stage", "type", "regex"),
}, nil
}
// parseRegexConfig processes an incoming configuration into a RegexConfig
func parseRegexConfig(config interface{}) (*RegexConfig, error) {
cfg := &RegexConfig{}
err := mapstructure.Decode(config, cfg)
if err != nil {
return nil, err
}
return cfg, nil
}
// Process implements Stage
func (r *regexStage) Process(labels model.LabelSet, extracted map[string]interface{}, t *time.Time, entry *string) {
// If a source key is provided, the regex stage should process it
// from the extracted map, otherwise should fallback to the entry
input := entry
if r.cfg.Source != nil {
if _, ok := extracted[*r.cfg.Source]; !ok {
if Debug {
level.Debug(r.logger).Log("msg", "source does not exist in the set of extracted values", "source", *r.cfg.Source)
}
return
}
value, err := getString(extracted[*r.cfg.Source])
if err != nil {
if Debug {
level.Debug(r.logger).Log("msg", "failed to convert source value to string", "source", *r.cfg.Source, "err", err, "type", reflect.TypeOf(extracted[*r.cfg.Source]))
}
return
}
input = &value
}
if input == nil {
if Debug {
level.Debug(r.logger).Log("msg", "cannot parse a nil entry")
}
return
}
match := r.expression.FindStringSubmatch(*input)
if match == nil {
if Debug {
level.Debug(r.logger).Log("msg", "regex did not match", "input", *input, "regex", r.expression)
}
return
}
for i, name := range r.expression.SubexpNames() {
if i != 0 && name != "" {
extracted[name] = match[i]
}
}
}
// Name implements Stage
func (r *regexStage) Name() string {
return StageTypeRegex
}