From a128482f1b9541a0c14f6ab58c0021e77e8120cb Mon Sep 17 00:00:00 2001 From: Chris Grindstaff Date: Thu, 11 Jul 2024 14:19:09 -0400 Subject: [PATCH] feat: enable ChangeLog plugin to monitor metric value change --- cmd/exporters/prometheus/prometheus.go | 15 ++-- cmd/exporters/prometheus/prometheus_test.go | 85 ++++++++++++++++++++- 2 files changed, 87 insertions(+), 13 deletions(-) diff --git a/cmd/exporters/prometheus/prometheus.go b/cmd/exporters/prometheus/prometheus.go index 7b25546cb..2a2e3df81 100644 --- a/cmd/exporters/prometheus/prometheus.go +++ b/cmd/exporters/prometheus/prometheus.go @@ -342,16 +342,11 @@ func (p *Prometheus) render(data *matrix.Matrix) ([][]byte, exporter.Stats) { // For example, it might indicate that 'volume_size_total' has been updated. // If a global prefix for the exporter is defined, we need to amend the metric name with this prefix. if p.globalPrefix != "" && data.Object == changelog.ObjectChangeLog { - categoryIsMetric := false - for label, value := range instance.GetLabels() { - if label == changelog.Category && value == changelog.Metric { - categoryIsMetric = true - break - } - } - if categoryIsMetric { - if value, ok := instance.GetLabels()[changelog.Track]; ok { - instance.GetLabels()[changelog.Track] = p.globalPrefix + value + if categoryValue, ok := instance.GetLabels()[changelog.Category]; ok { + if categoryValue == changelog.Metric { + if tracked, ok := instance.GetLabels()[changelog.Track]; ok { + instance.GetLabels()[changelog.Track] = p.globalPrefix + tracked + } } } } diff --git a/cmd/exporters/prometheus/prometheus_test.go b/cmd/exporters/prometheus/prometheus_test.go index e2af74ac0..34e67b9ed 100644 --- a/cmd/exporters/prometheus/prometheus_test.go +++ b/cmd/exporters/prometheus/prometheus_test.go @@ -6,6 +6,7 @@ package prometheus import ( "bytes" + "github.com/google/go-cmp/cmp" "github.com/netapp/harvest/v2/cmd/poller/exporter" "github.com/netapp/harvest/v2/cmd/poller/options" "github.com/netapp/harvest/v2/pkg/conf" @@ -88,6 +89,32 @@ func BenchmarkEscape(b *testing.B) { } } +func setUpChangeMatrix() *matrix.Matrix { + m := matrix.New("change", "change", "change") + // Create a metric with a metric value change + log, _ := m.NewMetricUint64("log") + instance, _ := m.NewInstance("A") + _ = log.SetValueInt64(instance, 3) + instance.SetLabel("category", "metric") + instance.SetLabel("cluster", "umeng-aff300-01-02") + instance.SetLabel("object", "volume") + instance.SetLabel("op", "metric_change") + instance.SetLabel("track", "volume_size_total") + + // Create a metric with a label change + instance2, _ := m.NewInstance("B") + _ = log.SetValueInt64(instance2, 3) + instance2.SetLabel("category", "label") + instance2.SetLabel("cluster", "umeng-aff300-01-02") + instance2.SetLabel("new_value", "offline") + instance2.SetLabel("object", "volume") + instance2.SetLabel("old_value", "online") + instance2.SetLabel("op", "update") + instance2.SetLabel("track", "state") + + return m +} + func setUpMatrix(object string) *matrix.Matrix { m := matrix.New("bike", object, "bike") speed, _ := m.NewMetricUint64("max_speed") @@ -115,7 +142,7 @@ max_speed{} 3`}, for _, tt := range tests { t.Run(tt.prefix, func(t *testing.T) { - p, err := setupPrometheusExporter() + p, err := setUpPrometheusExporter("") if err != nil { t.Errorf("expected nil, got %v", err) } @@ -142,14 +169,66 @@ max_speed{} 3`}, } } -func setupPrometheusExporter() (exporter.Exporter, error) { +func TestGlobalPrefixWithChangelog(t *testing.T) { + + type test struct { + prefix string + want string + } + + tests := []test{ + {"prefix", ` +netapp_change_log{category="label",cluster="umeng-aff300-01-02",new_value="offline",object="volume",old_value="online",op="update",track="state"} 3 +netapp_change_log{category="metric",cluster="umeng-aff300-01-02",object="volume",op="metric_change",track="netapp_volume_size_total"} 3`}, + } + + for _, tt := range tests { + t.Run(tt.prefix, func(t *testing.T) { + p, err := setUpPrometheusExporter("netapp") + + if err != nil { + t.Errorf("expected nil, got %v", err) + } + m := setUpChangeMatrix() + + _, err = p.Export(m) + if err != nil { + t.Errorf("expected nil, got %v", err) + } + + prom := p.(*Prometheus) + var lines []string + for _, metrics := range prom.cache.Get() { + for _, metric := range metrics { + lines = append(lines, string(metric)) + } + } + + slices.Sort(lines) + diff := cmp.Diff(strings.TrimSpace(tt.want), strings.Join(lines, "\n")) + if diff != "" { + t.Errorf("Mismatch (-got +want):\n%s", diff) + } + }) + } +} + +func setUpPrometheusExporter(prefix string) (exporter.Exporter, error) { + absExp := exporter.New( "Prometheus", "prom1", &options.Options{PromPort: 1}, - conf.Exporter{IsTest: true}, + conf.Exporter{ + IsTest: true, + SortLabels: true, + }, nil, ) + + if prefix != "" { + absExp.Params.GlobalPrefix = &prefix + } p := New(absExp) err := p.Init() return p, err