Alerting: Filter out private labels before writing recording rules (#109295)

This commit is contained in:
Alexander Akhmetov
2025-08-07 17:25:12 +02:00
committed by GitHub
parent 696b1bcc69
commit 89d6756c67
4 changed files with 132 additions and 1 deletions
+31
View File
@@ -124,6 +124,9 @@ func (r RuleType) String() string {
}
const (
// When adding new constants with __*__ pattern that are labels,
// you need to add them to PrivateLabelsToFilter below if they should be filtered out from recording rules.
// Annotations are actually a set of labels, so technically this is the label name of an annotation.
DashboardUIDAnnotation = "__dashboardUid__"
PanelIDAnnotation = "__panelId__"
@@ -199,6 +202,15 @@ var (
AutogeneratedRouteReceiverNameLabel: {},
AutogeneratedRouteSettingsHashLabel: {},
}
// PrivateLabelsToFilter are internal labels that should be filtered out from recording rules.
// These labels are used internally by Grafana and should not appear in the final metrics.
PrivateLabelsToFilter = map[string]struct{}{
ConvertedPrometheusRuleLabel: {},
AutogeneratedRouteLabel: {},
AutogeneratedRouteReceiverNameLabel: {},
AutogeneratedRouteSettingsHashLabel: {},
}
)
// AlertRuleGroup is the base model for a rule group in unified alerting.
@@ -407,6 +419,25 @@ func WithoutInternalLabels() LabelOption {
}
}
// WithoutPrivateLabels returns a new map without private labels.
// It filters out labels that are in PrivateLabelsToFilter set.
func WithoutPrivateLabels(labels map[string]string) map[string]string {
if labels == nil {
return nil
}
result := make(map[string]string, len(labels))
for k, v := range labels {
// Check if it's in the explicit filter list
if _, shouldFilter := PrivateLabelsToFilter[k]; shouldFilter {
continue
}
result[k] = v
}
return result
}
func (alertRule *AlertRule) ImportedPrometheusRule() bool {
_, hasConvertedPrometheusRuleLabel := alertRule.GetLabels()[ConvertedPrometheusRuleLabel]
return hasConvertedPrometheusRuleLabel || alertRule.HasPrometheusRuleDefinition()
@@ -1348,3 +1348,54 @@ func TestAlertRule_ImportedPrometheusRule(t *testing.T) {
})
}
}
func TestWithoutPrivateLabels(t *testing.T) {
tests := []struct {
name string
input map[string]string
expected map[string]string
}{
{
name: "nil map",
input: nil,
expected: nil,
},
{
name: "empty map",
input: map[string]string{},
expected: map[string]string{},
},
{
name: "removes only specific private labels",
input: map[string]string{
ConvertedPrometheusRuleLabel: "removed",
AutogeneratedRouteLabel: "removed",
AutogeneratedRouteReceiverNameLabel: "removed",
AutogeneratedRouteSettingsHashLabel: "removed",
DashboardUIDAnnotation: "kept",
PanelIDAnnotation: "kept",
"__custom_label__": "kept", // User-defined labels with __ are kept
"normal_label": "kept",
"another_label": "kept",
},
expected: map[string]string{
"__custom_label__": "kept",
"normal_label": "kept",
"another_label": "kept",
DashboardUIDAnnotation: "kept",
PanelIDAnnotation: "kept",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
inputCopy := maps.Clone(tt.input)
result := WithoutPrivateLabels(tt.input)
require.Equal(t, tt.expected, result)
require.Equal(t, inputCopy, tt.input, "input map should not be modified")
})
}
}
@@ -272,8 +272,9 @@ func (r *recordingRule) tryEvaluation(ctx context.Context, ev *Evaluation, logge
return nil
}
filteredLabels := ngmodels.WithoutPrivateLabels(ev.rule.Labels)
writeStart := r.clock.Now()
err = r.writer.WriteDatasource(ctx, ev.rule.Record.TargetDatasourceUID, ev.rule.Record.Metric, ev.scheduledAt, frames, ev.rule.OrgID, ev.rule.Labels)
err = r.writer.WriteDatasource(ctx, ev.rule.Record.TargetDatasourceUID, ev.rule.Record.Metric, ev.scheduledAt, frames, ev.rule.OrgID, filteredLabels)
writeDur := r.clock.Now().Sub(writeStart)
if err != nil {
@@ -750,6 +750,54 @@ func testRecordingRule_Integration(t *testing.T, writeTarget *writer.TestRemoteW
require.Equal(t, "error", status.Health)
})
})
t.Run("rule with private labels filtered", func(t *testing.T) {
writeTarget.Reset()
rule := gen.With(withQueryForHealth("ok")).GenerateRef()
rule.Record.TargetDatasourceUID = dsUID
rule.Labels = map[string]string{
"normal_label": "value1",
"another_label": "value2",
models.AutogeneratedRouteLabel: "filtered",
models.AutogeneratedRouteReceiverNameLabel: "filtered",
"__user_custom__": "not_filtered",
"only_end__": "not_filtered",
}
ruleStore.PutRule(context.Background(), rule)
folderTitle := ruleStore.getNamespaceTitle(rule.NamespaceUID)
ruleFactory := ruleFactoryFromScheduler(sch)
process := ruleFactory.new(context.Background(), rule)
evalDoneChan := make(chan time.Time)
process.(*recordingRule).evalAppliedHook = func(_ models.AlertRuleKey, t time.Time) {
evalDoneChan <- t
}
now := time.Now()
go func() {
_ = process.Run()
}()
process.Eval(&Evaluation{
scheduledAt: now,
rule: rule,
folderTitle: folderTitle,
})
_ = waitForTimeChannel(t, evalDoneChan)
t.Run("write was performed with filtered labels", func(t *testing.T) {
require.Equal(t, 1, writeTarget.RequestsCount)
require.NotEmpty(t, writeTarget.LastRequestBody)
// Check that the body doesn't contain the private labels
require.NotContains(t, writeTarget.LastRequestBody, models.AutogeneratedRouteLabel)
require.NotContains(t, writeTarget.LastRequestBody, models.AutogeneratedRouteReceiverNameLabel)
require.Contains(t, writeTarget.LastRequestBody, "__user_custom__")
require.Contains(t, writeTarget.LastRequestBody, rule.Record.Metric)
})
})
}
func withQueryForHealth(health string) models.AlertRuleMutator {