diff --git a/pkg/metrics/extractor.go b/pkg/metrics/extractor.go index c6c3903..1f2ed47 100644 --- a/pkg/metrics/extractor.go +++ b/pkg/metrics/extractor.go @@ -1,7 +1,6 @@ package metrics import ( - "errors" "fmt" "github.com/hikhvar/mqtt2prometheus/pkg/config" @@ -21,7 +20,14 @@ func NewJSONObjectExtractor(p Parser) Extractor { if rawValue == nil { continue } - m, err := p.parseMetric(path, deviceID, rawValue) + + // Find a valid metrics config + config, found := p.findMetricConfig(path, deviceID) + if !found { + continue + } + + m, err := p.parseMetric(config, rawValue) if err != nil { return nil, fmt.Errorf("failed to parse valid metric value: %w", err) } @@ -32,17 +38,21 @@ func NewJSONObjectExtractor(p Parser) Extractor { } } -func NewMetricPerTopicExtractor(p Parser, metricName *config.Regexp) Extractor { +func NewMetricPerTopicExtractor(p Parser, metricNameRegex *config.Regexp) Extractor { return func(topic string, payload []byte, deviceID string) (MetricCollection, error) { - mName := metricName.GroupValue(topic, config.MetricNameRegexGroup) - if mName == "" { + metricName := metricNameRegex.GroupValue(topic, config.MetricNameRegexGroup) + if metricName == "" { return nil, fmt.Errorf("failed to find valid metric in topic path") } - m, err := p.parseMetric(mName, deviceID, string(payload)) + + // Find a valid metrics config + config, found := p.findMetricConfig(metricName, deviceID) + if !found { + return nil, nil + } + + m, err := p.parseMetric(config, string(payload)) if err != nil { - if errors.Is(err, metricNotConfigured) { - return nil, nil - } return nil, fmt.Errorf("failed to parse metric: %w", err) } m.Topic = topic diff --git a/pkg/metrics/extractor_test.go b/pkg/metrics/extractor_test.go index adac098..4bdfe7c 100644 --- a/pkg/metrics/extractor_test.go +++ b/pkg/metrics/extractor_test.go @@ -25,6 +25,7 @@ func TestNewJSONObjectExtractor_parseMetric(t *testing.T) { args args want Metric wantErr bool + noValue bool }{ { name: "string value", @@ -78,6 +79,55 @@ func TestNewJSONObjectExtractor_parseMetric(t *testing.T) { IngestTime: testNow(), Topic: "topic", }, + }, { + name: "metric matching SensorNameFilter", + separator: ".", + fields: fields{ + map[string][]config.MetricConfig{ + "temperature": []config.MetricConfig{ + { + PrometheusName: "temperature", + MQTTName: "temperature", + ValueType: "gauge", + SensorNameFilter: *config.MustNewRegexp(".*22$"), + }, + }, + }, + }, + args: args{ + metricPath: "topic", + deviceID: "dht22", + value: "{\"temperature\": 8.5}", + }, + want: Metric{ + Description: prometheus.NewDesc("temperature", "", []string{"sensor", "topic"}, nil), + ValueType: prometheus.GaugeValue, + Value: 8.5, + IngestTime: testNow(), + Topic: "topic", + }, + }, { + name: "metric not matching SensorNameFilter", + separator: ".", + fields: fields{ + map[string][]config.MetricConfig{ + "temperature": []config.MetricConfig{ + { + PrometheusName: "temperature", + MQTTName: "temperature", + ValueType: "gauge", + SensorNameFilter: *config.MustNewRegexp(".*fail$"), + }, + }, + }, + }, + args: args{ + metricPath: "topic", + deviceID: "dht22", + value: "{\"temperature\": 8.5}", + }, + want: Metric{}, + noValue: true, }, } for _, tt := range tests { @@ -93,10 +143,15 @@ func TestNewJSONObjectExtractor_parseMetric(t *testing.T) { t.Errorf("parseMetric() error = %v, wantErr %v", err, tt.wantErr) return } - if len(got) != 1 { - t.Errorf("parseMetric() got = %v, want %v", nil, tt.want) + + if len(got) == 0 { + if !tt.noValue { + t.Errorf("parseMetric() got = %v, want %v", nil, tt.want) + } } else if !reflect.DeepEqual(got[0], tt.want) { t.Errorf("parseMetric() got = %v, want %v", got[0], tt.want) + } else if len(got) > 1 { + t.Errorf("unexpected result got = %v, want %v", got, tt.want) } }) } diff --git a/pkg/metrics/parser.go b/pkg/metrics/parser.go index e1f13a2..2482239 100644 --- a/pkg/metrics/parser.go +++ b/pkg/metrics/parser.go @@ -1,7 +1,6 @@ package metrics import ( - "errors" "fmt" "strconv" "time" @@ -9,12 +8,10 @@ import ( "github.com/hikhvar/mqtt2prometheus/pkg/config" ) -type metricNotConfiguredError error - -var metricNotConfigured metricNotConfiguredError = errors.New("metric not configured failed to parse") - type Parser struct { - separator string + separator string + // Maps the mqtt metric name to a list of configs + // The first that matches SensorNameFilter will be used metricConfigs map[string][]config.MetricConfig } @@ -39,7 +36,7 @@ func (p *Parser) config() map[string][]config.MetricConfig { // validMetric returns config matching the metric and deviceID // Second return value indicates if config was found. -func (p *Parser) validMetric(metric string, deviceID string) (config.MetricConfig, bool) { +func (p *Parser) findMetricConfig(metric string, deviceID string) (config.MetricConfig, bool) { for _, c := range p.metricConfigs[metric] { if c.SensorNameFilter.Match(deviceID) { return c, true @@ -50,12 +47,7 @@ func (p *Parser) validMetric(metric string, deviceID string) (config.MetricConfi // parseMetric parses the given value according to the given deviceID and metricPath. The config allows to // parse a metric value according to the device ID. -func (p *Parser) parseMetric(metricPath string, deviceID string, value interface{}) (Metric, error) { - cfg, cfgFound := p.validMetric(metricPath, deviceID) - if !cfgFound { - return Metric{}, metricNotConfigured - } - +func (p *Parser) parseMetric(cfg config.MetricConfig, value interface{}) (Metric, error) { var metricValue float64 if boolValue, ok := value.(bool); ok { diff --git a/pkg/metrics/parser_test.go b/pkg/metrics/parser_test.go index f712eb2..d23df61 100644 --- a/pkg/metrics/parser_test.go +++ b/pkg/metrics/parser_test.go @@ -411,7 +411,7 @@ func TestParser_parseMetric(t *testing.T) { args: args{ metricPath: "enabled", deviceID: "dht22", - value: metricNotConfigured, + value: []int{3}, }, wantErr: true, }, @@ -421,7 +421,17 @@ func TestParser_parseMetric(t *testing.T) { p := &Parser{ metricConfigs: tt.fields.metricConfigs, } - got, err := p.parseMetric(tt.args.metricPath, tt.args.deviceID, tt.args.value) + + // Find a valid metrics config + config, found := p.findMetricConfig(tt.args.metricPath, tt.args.deviceID) + if !found { + if !tt.wantErr { + t.Errorf("MetricConfig not found") + } + return + } + + got, err := p.parseMetric(config, tt.args.value) if (err != nil) != tt.wantErr { t.Errorf("parseMetric() error = %v, wantErr %v", err, tt.wantErr) return