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 <erhudy@users.noreply.github.com>
This commit is contained in:
Edmund Rhudy 2024-02-27 19:54:41 -05:00
parent cae614397a
commit f1a66c0713
4 changed files with 96 additions and 10 deletions

View file

@ -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.*.*.*"

View file

@ -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)

View file

@ -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)

View file

@ -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 == "" {