[v10.0.x] Alerting: Move rule UID from Loki stream labels into log lines (#70686)
Alerting: Move rule UID from Loki stream labels into log lines (#70637)
Move rule uid into log line to reduce cardinality
(cherry picked from commit 4aa477f48f)
Co-authored-by: William Wernert <william.wernert@gmail.com>
This commit is contained in:
co-authored by
William Wernert
parent
4f42ce8755
commit
83cd2900a7
@@ -151,15 +151,6 @@ func buildSelectors(query models.HistoryQuery) ([]Selector, error) {
|
|||||||
}
|
}
|
||||||
selectors[1] = selector
|
selectors[1] = selector
|
||||||
|
|
||||||
// Set the optional special selector rule_id
|
|
||||||
if query.RuleUID != "" {
|
|
||||||
rsel, err := NewSelector(RuleUIDLabel, "=", query.RuleUID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
selectors = append(selectors, rsel)
|
|
||||||
}
|
|
||||||
|
|
||||||
return selectors, nil
|
return selectors, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +236,6 @@ func statesToStream(rule history_model.RuleMeta, states []state.StateTransition,
|
|||||||
// System-defined labels take precedence over user-defined external labels.
|
// System-defined labels take precedence over user-defined external labels.
|
||||||
labels[StateHistoryLabelKey] = StateHistoryLabelValue
|
labels[StateHistoryLabelKey] = StateHistoryLabelValue
|
||||||
labels[OrgIDLabel] = fmt.Sprint(rule.OrgID)
|
labels[OrgIDLabel] = fmt.Sprint(rule.OrgID)
|
||||||
labels[RuleUIDLabel] = fmt.Sprint(rule.UID)
|
|
||||||
labels[GroupLabel] = fmt.Sprint(rule.Group)
|
labels[GroupLabel] = fmt.Sprint(rule.Group)
|
||||||
labels[FolderUIDLabel] = fmt.Sprint(rule.NamespaceUID)
|
labels[FolderUIDLabel] = fmt.Sprint(rule.NamespaceUID)
|
||||||
|
|
||||||
@@ -265,6 +255,7 @@ func statesToStream(rule history_model.RuleMeta, states []state.StateTransition,
|
|||||||
DashboardUID: rule.DashboardUID,
|
DashboardUID: rule.DashboardUID,
|
||||||
PanelID: rule.PanelID,
|
PanelID: rule.PanelID,
|
||||||
Fingerprint: labelFingerprint(sanitizedLabels),
|
Fingerprint: labelFingerprint(sanitizedLabels),
|
||||||
|
RuleUID: rule.UID,
|
||||||
InstanceLabels: sanitizedLabels,
|
InstanceLabels: sanitizedLabels,
|
||||||
}
|
}
|
||||||
if state.State.State == eval.Error {
|
if state.State.State == eval.Error {
|
||||||
@@ -309,6 +300,7 @@ type lokiEntry struct {
|
|||||||
DashboardUID string `json:"dashboardUID"`
|
DashboardUID string `json:"dashboardUID"`
|
||||||
PanelID int64 `json:"panelID"`
|
PanelID int64 `json:"panelID"`
|
||||||
Fingerprint string `json:"fingerprint"`
|
Fingerprint string `json:"fingerprint"`
|
||||||
|
RuleUID string `json:"ruleUID"`
|
||||||
// InstanceLabels is exactly the set of labels associated with the alert instance in Alertmanager.
|
// InstanceLabels is exactly the set of labels associated with the alert instance in Alertmanager.
|
||||||
// These should not be conflated with labels associated with log streams.
|
// These should not be conflated with labels associated with log streams.
|
||||||
InstanceLabels map[string]string `json:"labels"`
|
InstanceLabels map[string]string `json:"labels"`
|
||||||
@@ -378,6 +370,14 @@ func buildLogQuery(query models.HistoryQuery) (string, error) {
|
|||||||
|
|
||||||
logQL := selectorString(selectors)
|
logQL := selectorString(selectors)
|
||||||
|
|
||||||
|
if queryHasLogFilters(query) {
|
||||||
|
logQL = fmt.Sprintf("%s | json", logQL)
|
||||||
|
}
|
||||||
|
|
||||||
|
if query.RuleUID != "" {
|
||||||
|
logQL = fmt.Sprintf("%s | ruleUID=%q", logQL, query.RuleUID)
|
||||||
|
}
|
||||||
|
|
||||||
labelFilters := ""
|
labelFilters := ""
|
||||||
labelKeys := make([]string, 0, len(query.Labels))
|
labelKeys := make([]string, 0, len(query.Labels))
|
||||||
for k := range query.Labels {
|
for k := range query.Labels {
|
||||||
@@ -388,10 +388,11 @@ func buildLogQuery(query models.HistoryQuery) (string, error) {
|
|||||||
for _, k := range labelKeys {
|
for _, k := range labelKeys {
|
||||||
labelFilters += fmt.Sprintf(" | labels_%s=%q", k, query.Labels[k])
|
labelFilters += fmt.Sprintf(" | labels_%s=%q", k, query.Labels[k])
|
||||||
}
|
}
|
||||||
|
logQL += labelFilters
|
||||||
if labelFilters != "" {
|
|
||||||
logQL = fmt.Sprintf("%s | json%s", logQL, labelFilters)
|
|
||||||
}
|
|
||||||
|
|
||||||
return logQL, nil
|
return logQL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func queryHasLogFilters(query models.HistoryQuery) bool {
|
||||||
|
return query.RuleUID != "" || len(query.Labels) > 0
|
||||||
|
}
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ func TestRemoteLokiBackend(t *testing.T) {
|
|||||||
"folderUID": rule.NamespaceUID,
|
"folderUID": rule.NamespaceUID,
|
||||||
"group": rule.Group,
|
"group": rule.Group,
|
||||||
"orgID": fmt.Sprint(rule.OrgID),
|
"orgID": fmt.Sprint(rule.OrgID),
|
||||||
"ruleUID": rule.UID,
|
|
||||||
}
|
}
|
||||||
require.Equal(t, exp, res.Stream)
|
require.Equal(t, exp, res.Stream)
|
||||||
})
|
})
|
||||||
@@ -90,6 +89,20 @@ func TestRemoteLokiBackend(t *testing.T) {
|
|||||||
require.NotContains(t, res.Stream, "__private__")
|
require.NotContains(t, res.Stream, "__private__")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("includes ruleUID in log line", func(t *testing.T) {
|
||||||
|
rule := createTestRule()
|
||||||
|
l := log.NewNopLogger()
|
||||||
|
states := singleFromNormal(&state.State{
|
||||||
|
State: eval.Alerting,
|
||||||
|
Labels: data.Labels{"a": "b"},
|
||||||
|
})
|
||||||
|
|
||||||
|
res := statesToStream(rule, states, nil, l)
|
||||||
|
|
||||||
|
entry := requireSingleEntry(t, res)
|
||||||
|
require.Equal(t, rule.UID, entry.RuleUID)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("includes instance labels in log line", func(t *testing.T) {
|
t.Run("includes instance labels in log line", func(t *testing.T) {
|
||||||
rule := createTestRule()
|
rule := createTestRule()
|
||||||
l := log.NewNopLogger()
|
l := log.NewNopLogger()
|
||||||
@@ -210,12 +223,19 @@ func TestRemoteLokiBackend(t *testing.T) {
|
|||||||
exp: `{orgID="0",from="state-history"}`,
|
exp: `{orgID="0",from="state-history"}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "adds stream label filter for ruleUID and orgID",
|
name: "adds stream label filter for orgID",
|
||||||
query: models.HistoryQuery{
|
query: models.HistoryQuery{
|
||||||
RuleUID: "rule-uid",
|
OrgID: 123,
|
||||||
OrgID: 123,
|
|
||||||
},
|
},
|
||||||
exp: `{orgID="123",from="state-history",ruleUID="rule-uid"}`,
|
exp: `{orgID="123",from="state-history"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "filters ruleUID in log line",
|
||||||
|
query: models.HistoryQuery{
|
||||||
|
OrgID: 123,
|
||||||
|
RuleUID: "rule-uid",
|
||||||
|
},
|
||||||
|
exp: `{orgID="123",from="state-history"} | json | ruleUID="rule-uid"`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "filters instance labels in log line",
|
name: "filters instance labels in log line",
|
||||||
@@ -228,6 +248,17 @@ func TestRemoteLokiBackend(t *testing.T) {
|
|||||||
},
|
},
|
||||||
exp: `{orgID="123",from="state-history"} | json | labels_customlabel="customvalue" | labels_labeltwo="labelvaluetwo"`,
|
exp: `{orgID="123",from="state-history"} | json | labels_customlabel="customvalue" | labels_labeltwo="labelvaluetwo"`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "filters both instance labels + ruleUID",
|
||||||
|
query: models.HistoryQuery{
|
||||||
|
OrgID: 123,
|
||||||
|
RuleUID: "rule-uid",
|
||||||
|
Labels: map[string]string{
|
||||||
|
"customlabel": "customvalue",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exp: `{orgID="123",from="state-history"} | json | ruleUID="rule-uid" | labels_customlabel="customvalue"`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
|
|||||||
Reference in New Issue
Block a user