From f1a66c071376f7a0f516002a11254b8d1b9d7c87 Mon Sep 17 00:00:00 2001 From: Edmund Rhudy Date: Tue, 27 Feb 2024 19:54:41 -0500 Subject: [PATCH] Add global_labels option to defaults global_labels provides a way to add labels that will be added to all published metrics from the exporter. If a label defined in global_labels is also defined in a mapping, it will be overridden. Signed-off-by: Edmund Rhudy --- README.md | 7 ++-- pkg/exporter/exporter.go | 12 ++++++- pkg/exporter/exporter_test.go | 66 +++++++++++++++++++++++++++++++++++ pkg/mapper/mapper_defaults.go | 21 +++++++---- 4 files changed, 96 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index d6cd1f3..6a78b15 100644 --- a/README.md +++ b/README.md @@ -471,8 +471,8 @@ mappings: ### Global defaults -One may also set defaults for the observer type, histogram options, summary options, and match type. -These will be used by all mappings that do not define them. +One may also set defaults for the observer type, histogram options, summary options, match type, and global labels. +These will be used by all mappings that do not define them. Global labels will be overridden by labels derived from mappings. An option that can only be configured in `defaults` is `glob_disable_ordering`, which is `false` if omitted. By setting this to `true`, `glob` match type will not honor the occurance of rules in the mapping rules file and always treat `*` as lower priority than a concrete string. @@ -504,6 +504,9 @@ defaults: match_type: glob glob_disable_ordering: false ttl: 0 # metrics do not expire + global_labels: + example1: value1 + example2: value2 mappings: # This will be a histogram using the buckets set in `defaults`. - match: "test.timing.*.*.*" diff --git a/pkg/exporter/exporter.go b/pkg/exporter/exporter.go index f35cde5..79e5a56 100644 --- a/pkg/exporter/exporter.go +++ b/pkg/exporter/exporter.go @@ -96,7 +96,17 @@ func (b *Exporter) handleEvent(thisEvent event.Event) { help = mapping.HelpText } - prometheusLabels := thisEvent.Labels() + // labels on the event will override the global labels + var prometheusLabels prometheus.Labels + if b.Mapper.Defaults.GlobalLabels != nil { + prometheusLabels = b.Mapper.Defaults.GlobalLabels + for k, v := range thisEvent.Labels() { + prometheusLabels[k] = v + } + } else { + prometheusLabels = thisEvent.Labels() + } + if present { if mapping.Name == "" { level.Debug(b.Logger).Log("msg", "The mapping generates an empty metric name", "metric_name", thisEvent.MetricName(), "match", mapping.Match) diff --git a/pkg/exporter/exporter_test.go b/pkg/exporter/exporter_test.go index da08aee..c9f4409 100644 --- a/pkg/exporter/exporter_test.go +++ b/pkg/exporter/exporter_test.go @@ -334,6 +334,72 @@ mappings: } } +// TestGlobalLabels verifies that labels specified in defaults.global_labels +// are merged into the labels of the event and are overridden by event labels +func TestGlobalLabels(t *testing.T) { + codes := [3]string{"200", "300", "400"} + + events := make(chan event.Events) + go func() { + c := event.Events{ + &event.CounterEvent{ + CMetricName: "counter.test.200", + CValue: 1, + CLabels: make(map[string]string), + }, + &event.CounterEvent{ + CMetricName: "counter.test.300", + CValue: 1, + CLabels: make(map[string]string), + }, + &event.CounterEvent{ + CMetricName: "counter.test.400", + CValue: 1, + CLabels: map[string]string{"code": "should be overwritten"}, + }, + } + events <- c + close(events) + }() + + config := ` +defaults: + global_labels: + extra1: extra1_val + code: should_be_overwritten +mappings: +- match: counter.test.* + name: "counter_test" + labels: + code: $1 +` + + testMapper := &mapper.MetricMapper{} + err := testMapper.InitFromYAMLString(config) + if err != nil { + t.Fatalf("Config load error: %s %s", config, err) + } + + ex := NewExporter(prometheus.DefaultRegisterer, testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount) + ex.Listen(events) + + metrics, err := prometheus.DefaultGatherer.Gather() + if err != nil { + t.Fatalf("Cannot gather from DefaultGatherer: %v", err) + } + + labels := map[string]string{ + "extra1": "extra1_val", + } + + for _, code := range codes { + labels["code"] = code + if getFloat64(metrics, "counter_test", labels) == nil { + t.Fatalf("Could not find metrics for counter_test code %s", code) + } + } +} + func TestHonorLabels(t *testing.T) { metricName := "some_counter" events := make(chan event.Events) diff --git a/pkg/mapper/mapper_defaults.go b/pkg/mapper/mapper_defaults.go index c2112be..ade8790 100644 --- a/pkg/mapper/mapper_defaults.go +++ b/pkg/mapper/mapper_defaults.go @@ -13,15 +13,20 @@ package mapper -import "time" +import ( + "time" + + "github.com/prometheus/client_golang/prometheus" +) type MapperConfigDefaults struct { - ObserverType ObserverType `yaml:"observer_type"` - MatchType MatchType `yaml:"match_type"` - GlobDisableOrdering bool `yaml:"glob_disable_ordering"` - Ttl time.Duration `yaml:"ttl"` - SummaryOptions SummaryOptions `yaml:"summary_options"` - HistogramOptions HistogramOptions `yaml:"histogram_options"` + ObserverType ObserverType `yaml:"observer_type"` + MatchType MatchType `yaml:"match_type"` + GlobDisableOrdering bool `yaml:"glob_disable_ordering"` + Ttl time.Duration `yaml:"ttl"` + SummaryOptions SummaryOptions `yaml:"summary_options"` + HistogramOptions HistogramOptions `yaml:"histogram_options"` + GlobalLabels prometheus.Labels `yaml:"global_labels"` } // mapperConfigDefaultsAlias is used to unmarshal the yaml config into mapperConfigDefaults and allows deprecated fields @@ -35,6 +40,7 @@ type mapperConfigDefaultsAlias struct { Ttl time.Duration `yaml:"ttl"` SummaryOptions SummaryOptions `yaml:"summary_options"` HistogramOptions HistogramOptions `yaml:"histogram_options"` + GlobalLabels prometheus.Labels `yaml:"global_labels"` } // UnmarshalYAML is a custom unmarshal function to allow use of deprecated config keys @@ -52,6 +58,7 @@ func (d *MapperConfigDefaults) UnmarshalYAML(unmarshal func(interface{}) error) d.Ttl = tmp.Ttl d.SummaryOptions = tmp.SummaryOptions d.HistogramOptions = tmp.HistogramOptions + d.GlobalLabels = tmp.GlobalLabels // Use deprecated TimerType if necessary if tmp.ObserverType == "" {