Skip to content
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

Merged
merged 1 commit into from
Aug 14, 2017

Conversation

conr
Copy link
Contributor

@conr conr commented Aug 2, 2017

Closes #150

// Apply module config overrides to their corresponding metrics.
for name, params := range cfg.Overrides {
for _, metric := range out.Metrics {
if name == metric.Name {
Copy link
Contributor

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"`
Copy link
Contributor

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"`
Copy link
Contributor

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)
Copy link
Contributor

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)
Copy link
Contributor

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),
Copy link
Contributor

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))
Copy link
Contributor

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),
Copy link
Contributor

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)
Copy link
Contributor

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 {
Copy link
Contributor

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

@conr conr force-pushed the regex branch 2 times, most recently from 31d16ed to 2ab6e7c Compare August 2, 2017 15:51
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 {
Copy link
Contributor

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)
Copy link
Contributor

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"`
Copy link
Contributor

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 {
Copy link
Contributor

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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

regexp_extract, not module

if err := unmarshal((*plain)(c)); err != nil {
return err
}
if err := config.CheckOverflow(c.XXX, "module"); err != nil {
Copy link
Contributor

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)
Copy link
Contributor

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

collector.go Outdated
}
newMetric := prometheus.MustNewConstMetric(prometheus.NewDesc(metric.Name+name, metric.Help, labelnames, nil),
prometheus.GaugeValue, v, labelvalues...)
results = append(results, newMetric)
Copy link
Contributor

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

@conr conr force-pushed the regex branch 4 times, most recently from eb00fc5 to d7eeee1 Compare August 4, 2017 12:57
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)
Copy link
Contributor

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 {
Copy link
Contributor

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),
Copy link
Contributor

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 {
Copy link
Contributor

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)
Copy link
Contributor

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),
Copy link
Contributor

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

Name: "snIfOpticalLaneMonitoringTemperature",
Oid: "1.1.1.1.1",
Help: "HelpText",
RegexpExtracts: strMetrics,
Copy link
Contributor

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

Walk []string `yaml:"walk"`
Lookups []*Lookup `yaml:"lookups"`
type MetricOverrides struct {
RegexpExtracts map[string][]config.RegexpExtract `yaml:"regexp_extract,omitempty"`
Copy link
Contributor

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

@conr conr force-pushed the regex branch 6 times, most recently from 18a7c64 to 9550690 Compare August 8, 2017 12:25
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" {
Copy link
Contributor

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 {
Copy link
Contributor

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

"github.com/prometheus/snmp_exporter/config"
)

func TestPduToSample(t *testing.T) {
var regexpMatch, regexpNoMatch config.Regexp
regexpMatch.Regexp, _ = regexp.Compile(".*")
Copy link
Contributor

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

@conr conr force-pushed the regex branch 2 times, most recently from da44f88 to d48adb7 Compare August 8, 2017 17:13
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))
Copy link
Contributor

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)
Copy link
Contributor

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You missed this comment

},
},
},
expectedMetrics: []string{},
Copy link
Contributor

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

},
"Blank": []config.RegexpExtract{
{
Regex: config.Regexp{
Copy link
Contributor

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.

@conr conr force-pushed the regex branch 3 times, most recently from db0a174 to aa1bad1 Compare August 9, 2017 14:05
metric *config.Metric
oidToPdu map[string]gosnmp.SnmpPDU
expectedMetrics map[string]string
// expectedMetricDescs []string
Copy link
Contributor

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You missed this comment

@conr conr force-pushed the regex branch 3 times, most recently from 0fe2ed5 to 183c424 Compare August 14, 2017 14:17
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)
Copy link
Contributor

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.

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.
Copy link
Contributor

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

Walk []string `yaml:"walk"`
Lookups []*Lookup `yaml:"lookups"`
type MetricOverrides struct {
RegexpExtracts map[string][]config.RegexpExtract `yaml:"regex_extract,omitempty"`
Copy link
Contributor

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.

@brian-brazil
Copy link
Contributor

Format.md also needs updating.

@conr conr force-pushed the regex branch 2 times, most recently from 89545a6 to 36f6930 Compare August 14, 2017 15:30
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.
Copy link
Contributor

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"

@@ -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
Copy link
Contributor

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

metric.Name here too

@conr
Copy link
Contributor Author

conr commented Aug 14, 2017

@brian-brazil

@brian-brazil brian-brazil merged commit 6ea43b7 into prometheus:master Aug 14, 2017
@brian-brazil
Copy link
Contributor

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants