Alerting: Fix ambiguous handling of equals in labels when bucketing Loki state history streams (#65013)

* Use JSON instead of data.Labels string format as label repr

* Drop debug log line
This commit is contained in:
Alexander Weaver 2023-03-21 12:33:27 -05:00 committed by GitHub
parent bfb0dde4a8
commit cc7e5ce62e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 2 deletions

View File

@ -242,7 +242,7 @@ func merge(res queryRes, ruleUID string) (*data.Frame, error) {
}
func statesToStreams(rule history_model.RuleMeta, states []state.StateTransition, externalLabels map[string]string, logger log.Logger) []stream {
buckets := make(map[string][]sample) // label repr -> entries
buckets := make(map[string][]sample) // label repr (JSON) -> entries
for _, state := range states {
if !shouldRecord(state) {
continue
@ -254,7 +254,12 @@ func statesToStreams(rule history_model.RuleMeta, states []state.StateTransition
labels[RuleUIDLabel] = fmt.Sprint(rule.UID)
labels[GroupLabel] = fmt.Sprint(rule.Group)
labels[FolderUIDLabel] = fmt.Sprint(rule.NamespaceUID)
repr := labels.String()
lblJsn, err := json.Marshal(labels)
if err != nil {
logger.Error("Failed to marshal labels to JSON", "error", err)
continue
}
repr := string(lblJsn)
entry := lokiEntry{
SchemaVersion: 1,

View File

@ -323,6 +323,29 @@ grafana_alerting_state_history_writes_total{org="1"} 2
require.NoError(t, err)
require.Nil(t, req.lastRequest)
})
t.Run("succeeds with special chars in labels", func(t *testing.T) {
req := NewFakeRequester()
loki := createTestLokiBackend(req, metrics.NewHistorianMetrics(prometheus.NewRegistry()))
rule := createTestRule()
states := singleFromNormal(&state.State{
State: eval.Alerting,
Labels: data.Labels{
"dots": "contains.dot",
"equals": "contains=equals",
"emoji": "contains🤔emoji",
},
})
err := <-loki.Record(context.Background(), rule, states)
require.NoError(t, err)
require.Contains(t, "/loki/api/v1/push", req.lastRequest.URL.Path)
sent := string(readBody(t, req.lastRequest))
require.Contains(t, sent, "contains.dot")
require.Contains(t, sent, "contains=equals")
require.Contains(t, sent, "contains🤔emoji")
})
}
func createTestLokiBackend(req client.Requester, met *metrics.Historian) *RemoteLokiBackend {
@ -378,3 +401,11 @@ func badResponse() *http.Response {
Header: make(http.Header, 0),
}
}
func readBody(t *testing.T, req *http.Request) []byte {
t.Helper()
val, err := io.ReadAll(req.Body)
require.NoError(t, err)
return val
}