-
Notifications
You must be signed in to change notification settings - Fork 641
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extract numeric time series from string values #212
Conversation
generator/tree.go
Outdated
// Apply module config overrides to their corresponding metrics. | ||
for name, params := range cfg.Overrides { | ||
for _, metric := range out.Metrics { | ||
if name == metric.Name { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overrides may be provided by oid or name
config/config.go
Outdated
@@ -256,6 +253,60 @@ func (c *Auth) UnmarshalYAML(unmarshal func(interface{}) error) error { | |||
return nil | |||
} | |||
|
|||
type MetricOverrides struct { | |||
StringMetrics map[string][]StringMetric `yaml:"string_metrics,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the collector level there should only be this setting, not the whole MetricOverrides as the rest of those will only exist in the generator.
config/config.go
Outdated
@@ -256,6 +253,60 @@ func (c *Auth) UnmarshalYAML(unmarshal func(interface{}) error) error { | |||
return nil | |||
} | |||
|
|||
type MetricOverrides struct { | |||
StringMetrics map[string][]StringMetric `yaml:"string_metrics,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think string_metrics quite captures it, we have other string metrics. regexp_extract or similar would be better.
collector.go
Outdated
@@ -154,7 +154,10 @@ PduLoop: | |||
} | |||
if head.metric != nil { | |||
// Found a match. | |||
ch <- pduToSample(oidList[i+1:], &pdu, head.metric, oidToPdu) | |||
samples := pduToSample(oidList[i+1:], &pdu, head.metric, oidToPdu) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's really pduToSamples now
collector.go
Outdated
if len(strMetricSlice) > 0 { | ||
for _, strMetric := range strMetricSlice { | ||
value := strMetric.Regex.ReplaceAllString(pduValue, strMetric.Value) | ||
v, _ := strconv.ParseFloat(value, 64) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to check for an error here
collector.go
Outdated
for _, strMetric := range strMetricSlice { | ||
value := strMetric.Regex.ReplaceAllString(pduValue, strMetric.Value) | ||
v, _ := strconv.ParseFloat(value, 64) | ||
newMetric := prometheus.MustNewConstMetric(prometheus.NewDesc(metric.Name+"_"+name, metric.Help, []string{""}, nil), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't add an underscore here
collector.go
Outdated
@@ -192,6 +196,9 @@ func pduToSample(indexOids []int, pdu *gosnmp.SnmpPDU, metric *config.Metric, oi | |||
t = prometheus.GaugeValue | |||
value = 1.0 | |||
stringType = true | |||
if len(metric.Overrides.StringMetrics) > 0 { | |||
res = generateOverrideMetrics(metric, pdu.Value.(string)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You still need all the label logic below to apply. I'd reorder the code so this flows more naturally.
collector.go
Outdated
for _, strMetric := range strMetricSlice { | ||
value := strMetric.Regex.ReplaceAllString(pduValue, strMetric.Value) | ||
v, _ := strconv.ParseFloat(value, 64) | ||
newMetric := prometheus.MustNewConstMetric(prometheus.NewDesc(metric.Name+"_"+name, metric.Help, []string{""}, nil), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you have an empty labelname?
collector.go
Outdated
for name, strMetricSlice := range metric.Overrides.StringMetrics { | ||
if len(strMetricSlice) > 0 { | ||
for _, strMetric := range strMetricSlice { | ||
value := strMetric.Regex.ReplaceAllString(pduValue, strMetric.Value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to check that the regex matches before doing subsitution.
collector.go
Outdated
func generateOverrideMetrics(metric *config.Metric, pduValue string) []prometheus.Metric { | ||
results := []prometheus.Metric{} | ||
for name, strMetricSlice := range metric.Overrides.StringMetrics { | ||
if len(strMetricSlice) > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for this, the for already handles this correctly
31d16ed
to
2ab6e7c
Compare
collector.go
Outdated
// If the name is already an index, we do not need to set it again. | ||
if stringType { | ||
// For strings we put the value as a label with the same name as the metric. | ||
// If the name is already an index, we do not need to set it again. | ||
if _, ok := labels[metric.Name]; !ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code doesn't apply if we're applying the regexes
collector.go
Outdated
if _, ok := labels[metric.Name]; !ok { | ||
labelnames = append(labelnames, metric.Name) | ||
labelvalues = append(labelvalues, pduValueAsString(pdu, metric.Type)) | ||
} | ||
if len(metric.Overrides) > 0 { | ||
res = generateOverrideMetrics(metric, pdu.Value.(string), labelnames, labelvalues) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd return here
config/config.go
Outdated
Help string `yaml:"help"` | ||
Indexes []*Index `yaml:"indexes,omitempty"` | ||
Lookups []*Lookup `yaml:"lookups,omitempty"` | ||
Overrides map[string][]RegexpExtract `yaml:"regexp_extract,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The field should be called RegexpExtracts, not overrides
config/config.go
Outdated
} | ||
|
||
// UnmarshalYAML implements the yaml.Unmarshaler interface. | ||
func (re *Regexp) UnmarshalYAML(unmarshal func(interface{}) error) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure to add an unmarshalling test for a config that's missing the regex, we've had issues there in the past
config/config.go
Outdated
if err := unmarshal((*plain)(c)); err != nil { | ||
return err | ||
} | ||
if err := CheckOverflow(c.XXX, "module"); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
regexp_extract, not module
generator/config.go
Outdated
if err := unmarshal((*plain)(c)); err != nil { | ||
return err | ||
} | ||
if err := config.CheckOverflow(c.XXX, "module"); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overrides
collector.go
Outdated
value := strMetric.Regex.ReplaceAllString(pduValue, strMetric.Value) | ||
v, err := strconv.ParseFloat(value, 64) | ||
if err != nil { | ||
log.Errorf("Error parsing float64 from value: %v", value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be a debug log at most, and you need to continue
when it happens and try the next regex
collector.go
Outdated
results := []prometheus.Metric{} | ||
for name, strMetricSlice := range metric.Overrides { | ||
for _, strMetric := range strMetricSlice { | ||
if strMetric.Regex.MatchString(pduValue) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Look at https://github.com/prometheus/prometheus/blob/master/relabel/relabel.go#L57 for how to approach this
collector.go
Outdated
} | ||
newMetric := prometheus.MustNewConstMetric(prometheus.NewDesc(metric.Name+name, metric.Help, labelnames, nil), | ||
prometheus.GaugeValue, v, labelvalues...) | ||
results = append(results, newMetric) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we've gotten this far, we need to break
eb00fc5
to
d7eeee1
Compare
collector.go
Outdated
// If the name is already an index, we do not need to set it again. | ||
if stringType { | ||
if len(metric.RegexpExtracts) > 0 { | ||
return generateOverrideMetrics(metric, pdu.Value.(string), labelnames, labelvalues) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should have a test where the value is a number rather than a string.
collector.go
Outdated
return prometheus.MustNewConstMetric(prometheus.NewDesc(metric.Name, metric.Help, labelnames, nil), | ||
t, value, labelvalues...) | ||
|
||
if len(res) == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need this check
collector.go
Outdated
t, value, labelvalues...) | ||
|
||
if len(res) == 0 { | ||
res = append(res, prometheus.MustNewConstMetric(prometheus.NewDesc(metric.Name, metric.Help, labelnames, nil), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You know there's only one element in the list, you can create a literal here.
collector.go
Outdated
return res | ||
} | ||
|
||
func generateOverrideMetrics(metric *config.Metric, pduValue string, labelnames, labelvalues []string) []prometheus.Metric { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is applying the regex extracts, not all overrides
collector.go
Outdated
for name, strMetricSlice := range metric.RegexpExtracts { | ||
for _, strMetric := range strMetricSlice { | ||
if strMetric.Regex.MatchString(pduValue) { | ||
value := strMetric.Regex.ReplaceAllString(pduValue, strMetric.Value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to use the same logic as relabelling does so there's full regex functionality.
collector.go
Outdated
log.Debugf("Error parsing float64 from value: %v", value) | ||
continue | ||
} | ||
newMetric := prometheus.MustNewConstMetric(prometheus.NewDesc(metric.Name+name, metric.Help, labelnames, nil), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the Help you can mention that this was regex extracted
collector_test.go
Outdated
Name: "snIfOpticalLaneMonitoringTemperature", | ||
Oid: "1.1.1.1.1", | ||
Help: "HelpText", | ||
RegexpExtracts: strMetrics, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for an extra variable, you can put this in literally
generator/config.go
Outdated
Walk []string `yaml:"walk"` | ||
Lookups []*Lookup `yaml:"lookups"` | ||
type MetricOverrides struct { | ||
RegexpExtracts map[string][]config.RegexpExtract `yaml:"regexp_extract,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'd said to remove the p
18a7c64
to
9550690
Compare
collector.go
Outdated
// If the name is already an index, we do not need to set it again. | ||
if stringType { | ||
if len(metric.RegexpExtracts) > 0 { | ||
if reflect.TypeOf(pdu.Value).Name() != "string" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need reflection here, you can do
v, ok := pdu.Value.(string)
And then check ok
.
collector.go
Outdated
continue | ||
} | ||
res := strMetric.Regex.ExpandString([]byte{}, strMetric.Value, pduValue, indexes) | ||
if len(res) == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need this, the parsing should catch it
collector_test.go
Outdated
"github.com/prometheus/snmp_exporter/config" | ||
) | ||
|
||
func TestPduToSample(t *testing.T) { | ||
var regexpMatch, regexpNoMatch config.Regexp | ||
regexpMatch.Regexp, _ = regexp.Compile(".*") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put these inline in the struct, it's easier to understand the tests that way. You can use MustCompile
da44f88
to
d48adb7
Compare
collector.go
Outdated
if len(metric.RegexpExtracts) > 0 { | ||
v, ok := pdu.Value.(string) | ||
if !ok { | ||
log.Errorf("Invalid PDU value type: got %v, want string", reflect.TypeOf(pdu.Value)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could use %T here which will pull out the type
collector.go
Outdated
res := strMetric.Regex.ExpandString([]byte{}, strMetric.Value, pduValue, indexes) | ||
v, err := strconv.ParseFloat(string(res), 64) | ||
if err != nil { | ||
log.Debugf("Error parsing float64 from value: %v", res) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Including the metric name here would be useful, plus other log lines where you can
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You missed this comment
collector_test.go
Outdated
}, | ||
}, | ||
}, | ||
expectedMetrics: []string{}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put this on the others too
collector_test.go
Outdated
}, | ||
"Blank": []config.RegexpExtract{ | ||
{ | ||
Regex: config.Regexp{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you test multiple regexes too? We want to ensure first match wins.
db0a174
to
aa1bad1
Compare
collector_test.go
Outdated
metric *config.Metric | ||
oidToPdu map[string]gosnmp.SnmpPDU | ||
expectedMetrics map[string]string | ||
// expectedMetricDescs []string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this commented out?
collector.go
Outdated
res := strMetric.Regex.ExpandString([]byte{}, strMetric.Value, pduValue, indexes) | ||
v, err := strconv.ParseFloat(string(res), 64) | ||
if err != nil { | ||
log.Debugf("Error parsing float64 from value: %v", res) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You missed this comment
0fe2ed5
to
183c424
Compare
collector.go
Outdated
for _, strMetric := range strMetricSlice { | ||
indexes := strMetric.Regex.FindStringSubmatchIndex(pduValue) | ||
if indexes == nil { | ||
log.Debugf("No match found for regexp: %v against value: %v for metric %v", strMetric.Regex.String(), pduValue, name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
name
is the name of the extract, not the metric. You want metric.Name
.
generator/README.md
Outdated
metricName: | ||
regexp_extract: | ||
Temp: # A new metric will be created appending this to the metricName to become metricNameTemp. | ||
- regex: '.*' # Regex to extract a value from the returned SNMP walks's value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the example to make sense, this should be (.*) so that there is a $1
Also mention it's a float64 value
generator/config.go
Outdated
Walk []string `yaml:"walk"` | ||
Lookups []*Lookup `yaml:"lookups"` | ||
type MetricOverrides struct { | ||
RegexpExtracts map[string][]config.RegexpExtract `yaml:"regex_extract,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The field name here doesn't match the docs or unittest.
Format.md also needs updating. |
89545a6
to
36f6930
Compare
generator/README.md
Outdated
regex_extracts: | ||
Temp: # A new metric will be created appending this to the metricName to become metricNameTemp. | ||
- regex: '(.*)' # Regex to extract a value from the returned SNMP walks's value. | ||
value: '$1' # Defaults to $1, float64. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"the result will be parsed as a float64"
generator/FORMAT.md
Outdated
@@ -45,4 +45,11 @@ module_name: | |||
oid: 1.3.6.1.2.1.2.2.1.2 # OID to look under. | |||
labelname: ifDescr # Output label name. | |||
type: OctetString # Type of output object. | |||
# Overrides allows for per-module overrides of bits of MIBs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't the format of the exporter's yml, it's just the regex_extracts.
collector.go
Outdated
res := strMetric.Regex.ExpandString([]byte{}, strMetric.Value, pduValue, indexes) | ||
v, err := strconv.ParseFloat(string(res), 64) | ||
if err != nil { | ||
log.Debugf("Error parsing float64 from value: %v for metric: %v", res, name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
metric.Name here too
Thanks! |
Closes #150