Alerting: Add datasource_uid label to DatasourceNoData alerts (#41621)

This commit is contained in:
George Robinson 2021-11-16 10:03:18 +00:00 committed by GitHub
parent f49e08cb11
commit d363e19517
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 72 additions and 6 deletions

View File

@ -7,6 +7,7 @@ import (
"fmt"
"runtime/debug"
"sort"
"strconv"
"time"
"github.com/grafana/grafana/pkg/expr/classic"
@ -49,6 +50,9 @@ func (e *invalidEvalResultFormatError) Unwrap() error {
type ExecutionResults struct {
Error error
// NoData contains the DatasourceUID for RefIDs that returned no data.
NoData map[string]string
Results data.Frames
}
@ -165,17 +169,13 @@ type NumberValueCapture struct {
}
func executeCondition(ctx AlertExecCtx, c *models.Condition, now time.Time, exprService *expr.Service) ExecutionResults {
result := ExecutionResults{}
execResp, err := executeQueriesAndExpressions(ctx, c.Data, now, exprService)
if err != nil {
return ExecutionResults{Error: err}
}
// eval captures for the '__value_string__' annotation and the Value property of the API response.
captures := make([]NumberValueCapture, 0, len(execResp.Responses))
captureVal := func(refID string, labels data.Labels, value *float64) {
captures = append(captures, NumberValueCapture{
Var: refID,
@ -184,7 +184,28 @@ func executeCondition(ctx AlertExecCtx, c *models.Condition, now time.Time, expr
})
}
// datasourceUIDsForRefIDs is a short-lived lookup table of RefID to DatasourceUID
// for efficient lookups of the DatasourceUID when a RefID returns no data
datasourceUIDsForRefIDs := make(map[string]string)
for _, next := range c.Data {
datasourceUIDsForRefIDs[next.RefID] = next.DatasourceUID
}
// datasourceExprUID is a special DatasourceUID for expressions
datasourceExprUID := strconv.FormatInt(expr.DatasourceID, 10)
var result ExecutionResults
for refID, res := range execResp.Responses {
if len(res.Frames) == 0 {
// to ensure that NoData is consistent with Results we do not initialize NoData
// unless there is at least one RefID that returned no data
if result.NoData == nil {
result.NoData = make(map[string]string)
}
if s, ok := datasourceUIDsForRefIDs[refID]; ok && s != datasourceExprUID {
result.NoData[refID] = s
}
}
// for each frame within each response, the response can contain several data types including time-series data.
// For now, we favour simplicity and only care about single scalar values.
for _, frame := range res.Frames {
@ -281,10 +302,10 @@ func evaluateExecutionResult(execResults ExecutionResults, ts time.Time) Results
})
}
appendNoData := func(l data.Labels) {
appendNoData := func(labels data.Labels) {
evalResults = append(evalResults, Result{
State: NoData,
Instance: l,
Instance: labels,
EvaluatedAt: ts,
EvaluationDuration: time.Since(ts),
})
@ -295,6 +316,17 @@ func evaluateExecutionResult(execResults ExecutionResults, ts time.Time) Results
return evalResults
}
if len(execResults.NoData) > 0 {
m := make(map[string]struct{})
for _, datasourceUID := range execResults.NoData {
if _, ok := m[datasourceUID]; !ok {
appendNoData(data.Labels{"datasource_uid": datasourceUID})
m[datasourceUID] = struct{}{}
}
}
return evalResults
}
if len(execResults.Results) == 0 {
appendNoData(nil)
return evalResults

View File

@ -276,6 +276,40 @@ func TestEvaluateExecutionResult(t *testing.T) {
},
},
},
{
desc: "no data for Ref ID will produce NoData result",
execResults: ExecutionResults{
NoData: map[string]string{"A": "1"},
},
expectResultLength: 1,
expectResults: Results{
{
Instance: data.Labels{"datasource_uid": "1"},
State: NoData,
},
},
},
{
desc: "no data for Ref IDs will produce NoData result for each data source",
execResults: ExecutionResults{
NoData: map[string]string{
"A": "1",
"B": "1",
"C": "2",
},
},
expectResultLength: 2,
expectResults: Results{
{
Instance: data.Labels{"datasource_uid": "1"},
State: NoData,
},
{
Instance: data.Labels{"datasource_uid": "2"},
State: NoData,
},
},
},
}
for _, tc := range cases {