diff --git a/modules/generator/registry/counter.go b/modules/generator/registry/counter.go index 907a9e57b1f..25daa9abcb0 100644 --- a/modules/generator/registry/counter.go +++ b/modules/generator/registry/counter.go @@ -20,6 +20,8 @@ type counter struct { onAddSeries func(count uint32) bool onRemoveSeries func(count uint32) + + externalLabels map[string]string } type counterSeries struct { @@ -31,6 +33,9 @@ type counterSeries struct { // to the desired value. This avoids Prometheus throwing away the first // value in the series, due to the transition from null -> x. firstSeries *atomic.Bool + + lb *labels.Builder + baseLabels labels.Labels } var ( @@ -46,7 +51,7 @@ func (co *counterSeries) registerSeenSeries() { co.firstSeries.Store(false) } -func newCounter(name string, onAddSeries func(uint32) bool, onRemoveSeries func(count uint32)) *counter { +func newCounter(name string, onAddSeries func(uint32) bool, onRemoveSeries func(count uint32), externalLabels map[string]string) *counter { if onAddSeries == nil { onAddSeries = func(uint32) bool { return true @@ -61,6 +66,7 @@ func newCounter(name string, onAddSeries func(uint32) bool, onRemoveSeries func( series: make(map[uint64]*counterSeries), onAddSeries: onAddSeries, onRemoveSeries: onRemoveSeries, + externalLabels: externalLabels, } } @@ -98,11 +104,24 @@ func (c *counter) Inc(labelValueCombo *LabelValueCombo, value float64) { } func (c *counter) newSeries(labelValueCombo *LabelValueCombo, value float64) *counterSeries { + // base labels + baseLabels := make(labels.Labels, 0, 1+len(c.externalLabels)) + + // add external labels + for name, value := range c.externalLabels { + baseLabels = append(baseLabels, labels.Label{Name: name, Value: value}) + } + + // add metric name + baseLabels = append(baseLabels, labels.Label{Name: labels.MetricName, Value: c.metricName}) + return &counterSeries{ labels: labelValueCombo.getLabelPair(), value: atomic.NewFloat64(value), lastUpdated: atomic.NewInt64(time.Now().UnixMilli()), firstSeries: atomic.NewBool(true), + lb: labels.NewBuilder(baseLabels), + baseLabels: baseLabels, } } @@ -121,31 +140,12 @@ func (c *counter) collectMetrics(appender storage.Appender, timeMs int64, extern activeSeries = len(c.series) - labelsCount := 0 - if activeSeries > 0 && c.series[0] != nil { - labelsCount = len(c.series[0].labels.names) - } - - // base labels - baseLabels := make(labels.Labels, 0, 1+len(externalLabels)+labelsCount) - - // add external labels - for name, value := range externalLabels { - baseLabels = append(baseLabels, labels.Label{Name: name, Value: value}) - } - - // add metric name - baseLabels = append(baseLabels, labels.Label{Name: labels.MetricName, Value: c.metricName}) - - // TODO: avoid allocation on each collection - lb := labels.NewBuilder(baseLabels) - for _, s := range c.series { - lb.Reset(baseLabels) + s.lb.Reset(s.baseLabels) // set series-specific labels for i, name := range s.labels.names { - lb.Set(name, s.labels.values[i]) + s.lb.Set(name, s.labels.values[i]) } // If we are about to call Append for the first time on a series, we need @@ -155,14 +155,14 @@ func (c *counter) collectMetrics(appender storage.Appender, timeMs int64, extern // We set the timestamp of the init serie at the end of the previous minute, that way we ensure it ends in a // different aggregation interval to avoid be downsampled. endOfLastMinuteMs := getEndOfLastMinuteMs(timeMs) - _, err = appender.Append(0, lb.Labels(), endOfLastMinuteMs, 0) + _, err = appender.Append(0, s.lb.Labels(), endOfLastMinuteMs, 0) if err != nil { return } s.registerSeenSeries() } - _, err = appender.Append(0, lb.Labels(), timeMs, s.value.Load()) + _, err = appender.Append(0, s.lb.Labels(), timeMs, s.value.Load()) if err != nil { return } diff --git a/modules/generator/registry/counter_test.go b/modules/generator/registry/counter_test.go index a9d6b2f6bc0..b3bcaf9a9a1 100644 --- a/modules/generator/registry/counter_test.go +++ b/modules/generator/registry/counter_test.go @@ -18,7 +18,7 @@ func Test_counter(t *testing.T) { return true } - c := newCounter("my_counter", onAdd, nil) + c := newCounter("my_counter", onAdd, nil, map[string]string{"external_label": "external_value"}) c.Inc(newLabelValueCombo([]string{"label"}, []string{"value-1"}), 1.0) c.Inc(newLabelValueCombo([]string{"label"}, []string{"value-2"}), 2.0) @@ -28,10 +28,10 @@ func Test_counter(t *testing.T) { collectionTimeMs := time.Now().UnixMilli() endOfLastMinuteMs := getEndOfLastMinuteMs(collectionTimeMs) expectedSamples := []sample{ - newSample(map[string]string{"__name__": "my_counter", "label": "value-1"}, endOfLastMinuteMs, 0), - newSample(map[string]string{"__name__": "my_counter", "label": "value-1"}, collectionTimeMs, 1), - newSample(map[string]string{"__name__": "my_counter", "label": "value-2"}, endOfLastMinuteMs, 0), - newSample(map[string]string{"__name__": "my_counter", "label": "value-2"}, collectionTimeMs, 2), + newSample(map[string]string{"__name__": "my_counter", "label": "value-1", "external_label": "external_value"}, endOfLastMinuteMs, 0), + newSample(map[string]string{"__name__": "my_counter", "label": "value-1", "external_label": "external_value"}, collectionTimeMs, 1), + newSample(map[string]string{"__name__": "my_counter", "label": "value-2", "external_label": "external_value"}, endOfLastMinuteMs, 0), + newSample(map[string]string{"__name__": "my_counter", "label": "value-2", "external_label": "external_value"}, collectionTimeMs, 2), } collectMetricAndAssert(t, c, collectionTimeMs, nil, 2, expectedSamples, nil) @@ -59,7 +59,7 @@ func TestCounterDifferentLabels(t *testing.T) { return true } - c := newCounter("my_counter", onAdd, nil) + c := newCounter("my_counter", onAdd, nil, nil) c.Inc(newLabelValueCombo([]string{"label"}, []string{"value-1"}), 1.0) c.Inc(newLabelValueCombo([]string{"another_label"}, []string{"another_value"}), 2.0) @@ -84,7 +84,7 @@ func Test_counter_cantAdd(t *testing.T) { return canAdd } - c := newCounter("my_counter", onAdd, nil) + c := newCounter("my_counter", onAdd, nil, nil) // allow adding new series canAdd = true @@ -123,7 +123,7 @@ func Test_counter_removeStaleSeries(t *testing.T) { removedSeries++ } - c := newCounter("my_counter", nil, onRemove) + c := newCounter("my_counter", nil, onRemove, nil) timeMs := time.Now().UnixMilli() c.Inc(newLabelValueCombo([]string{"label"}, []string{"value-1"}), 1.0) @@ -161,7 +161,7 @@ func Test_counter_removeStaleSeries(t *testing.T) { } func Test_counter_externalLabels(t *testing.T) { - c := newCounter("my_counter", nil, nil) + c := newCounter("my_counter", nil, nil, map[string]string{"external_label": "external_value"}) c.Inc(newLabelValueCombo([]string{"label"}, []string{"value-1"}), 1.0) c.Inc(newLabelValueCombo([]string{"label"}, []string{"value-2"}), 2.0) @@ -174,11 +174,11 @@ func Test_counter_externalLabels(t *testing.T) { newSample(map[string]string{"__name__": "my_counter", "label": "value-2", "external_label": "external_value"}, endOfLastMinuteMs, 0), newSample(map[string]string{"__name__": "my_counter", "label": "value-2", "external_label": "external_value"}, collectionTimeMs, 2), } - collectMetricAndAssert(t, c, collectionTimeMs, map[string]string{"external_label": "external_value"}, 2, expectedSamples, nil) + collectMetricAndAssert(t, c, collectionTimeMs, nil, 2, expectedSamples, nil) } func Test_counter_concurrencyDataRace(t *testing.T) { - c := newCounter("my_counter", nil, nil) + c := newCounter("my_counter", nil, nil, nil) end := make(chan struct{}) @@ -224,7 +224,7 @@ func Test_counter_concurrencyDataRace(t *testing.T) { } func Test_counter_concurrencyCorrectness(t *testing.T) { - c := newCounter("my_counter", nil, nil) + c := newCounter("my_counter", nil, nil, nil) var wg sync.WaitGroup end := make(chan struct{}) diff --git a/modules/generator/registry/registry.go b/modules/generator/registry/registry.go index 4a5ea1725d6..4172f04c371 100644 --- a/modules/generator/registry/registry.go +++ b/modules/generator/registry/registry.go @@ -144,7 +144,7 @@ func (r *ManagedRegistry) NewLabelValueCombo(labels []string, values []string) * } func (r *ManagedRegistry) NewCounter(name string) Counter { - c := newCounter(name, r.onAddMetricSeries, r.onRemoveMetricSeries) + c := newCounter(name, r.onAddMetricSeries, r.onRemoveMetricSeries, r.externalLabels) r.registerMetric(c) return c }