mirror of
https://github.com/grafana/grafana.git
synced 2026-07-30 00:08:10 -05:00
Alerting: Add frames for all queries and expressions (#55609)
This commit is one of two commits to make the data frames for all queries and expressions in an alert rule available to the state package for rendering a graph. It renames Result to Condition, and creates an additional field called Results that is a map of Ref ID to data.Frames.
This commit is contained in:
@@ -76,12 +76,16 @@ func (e *invalidEvalResultFormatError) Unwrap() error {
|
||||
// ExecutionResults contains the unevaluated results from executing
|
||||
// a condition.
|
||||
type ExecutionResults struct {
|
||||
Error error
|
||||
// Condition contains the results of the condition
|
||||
Condition data.Frames
|
||||
|
||||
// Results contains the results of all queries, reduce and math expressions
|
||||
Results map[string]data.Frames
|
||||
|
||||
// NoData contains the DatasourceUID for RefIDs that returned no data.
|
||||
NoData map[string]string
|
||||
|
||||
Results data.Frames
|
||||
Error error
|
||||
}
|
||||
|
||||
// Results is a slice of evaluated alert instances states.
|
||||
@@ -101,20 +105,24 @@ func (evalResults Results) HasErrors() bool {
|
||||
type Result struct {
|
||||
Instance data.Labels
|
||||
State State // Enum
|
||||
// Error message for Error state. should be nil if State != Error.
|
||||
Error error
|
||||
EvaluatedAt time.Time
|
||||
EvaluationDuration time.Duration
|
||||
|
||||
// EvaluationString is a string representation of evaluation data such
|
||||
// as EvalMatches (from "classic condition"), and in the future from operations
|
||||
// like SSE "math".
|
||||
EvaluationString string
|
||||
// Error message for Error state. should be nil if State != Error.
|
||||
Error error
|
||||
|
||||
// Results contains the results of all queries, reduce and math expressions
|
||||
Results map[string]data.Frames
|
||||
|
||||
// Values contains the RefID and value of reduce and math expressions.
|
||||
// It does not contain values for classic conditions as the values
|
||||
// in classic conditions do not have a RefID.
|
||||
Values map[string]NumberValueCapture
|
||||
|
||||
EvaluatedAt time.Time
|
||||
EvaluationDuration time.Duration
|
||||
// EvaluationString is a string representation of evaluation data such
|
||||
// as EvalMatches (from "classic condition"), and in the future from operations
|
||||
// like SSE "math".
|
||||
EvaluationString string
|
||||
}
|
||||
|
||||
// State is an enum of the evaluation State for an alert instance.
|
||||
@@ -243,7 +251,7 @@ func queryDataResponseToExecutionResults(c models.Condition, execResp *backend.Q
|
||||
// datasourceExprUID is a special DatasourceUID for expressions
|
||||
datasourceExprUID := strconv.FormatInt(expr.DatasourceID, 10)
|
||||
|
||||
var result ExecutionResults
|
||||
result := ExecutionResults{Results: make(map[string]data.Frames)}
|
||||
for refID, res := range execResp.Responses {
|
||||
if len(res.Frames) == 0 {
|
||||
// to ensure that NoData is consistent with Results we do not initialize NoData
|
||||
@@ -270,12 +278,13 @@ func queryDataResponseToExecutionResults(c models.Condition, execResp *backend.Q
|
||||
}
|
||||
|
||||
if refID == c.Condition {
|
||||
result.Results = res.Frames
|
||||
result.Condition = res.Frames
|
||||
}
|
||||
result.Results[refID] = res.Frames
|
||||
}
|
||||
|
||||
// add capture values as data frame metadata to each result (frame) that has matching labels.
|
||||
for _, frame := range result.Results {
|
||||
for _, frame := range result.Condition {
|
||||
// classic conditions already have metadata set and only have one value, there's no need to add anything in this case.
|
||||
if frame.Meta != nil && frame.Meta.Custom != nil {
|
||||
if _, ok := frame.Meta.Custom.([]classic.EvalMatch); ok {
|
||||
@@ -422,12 +431,12 @@ func evaluateExecutionResult(execResults ExecutionResults, ts time.Time) Results
|
||||
return evalResults
|
||||
}
|
||||
|
||||
if len(execResults.Results) == 0 {
|
||||
if len(execResults.Condition) == 0 {
|
||||
appendNoData(nil)
|
||||
return evalResults
|
||||
}
|
||||
|
||||
for _, f := range execResults.Results {
|
||||
for _, f := range execResults.Condition {
|
||||
rowLen, err := f.RowLen()
|
||||
if err != nil {
|
||||
appendErrRes(&invalidEvalResultFormatError{refID: f.RefID, reason: "unable to get frame row length", err: err})
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestEvaluateExecutionResult(t *testing.T) {
|
||||
{
|
||||
desc: "zero valued single instance is single Normal state result",
|
||||
execResults: ExecutionResults{
|
||||
Results: []*data.Frame{
|
||||
Condition: []*data.Frame{
|
||||
data.NewFrame("", data.NewField("", nil, []*float64{ptr.Float64(0)})),
|
||||
},
|
||||
},
|
||||
@@ -44,7 +44,7 @@ func TestEvaluateExecutionResult(t *testing.T) {
|
||||
{
|
||||
desc: "non-zero valued single instance is single Alerting state result",
|
||||
execResults: ExecutionResults{
|
||||
Results: []*data.Frame{
|
||||
Condition: []*data.Frame{
|
||||
data.NewFrame("", data.NewField("", nil, []*float64{ptr.Float64(1)})),
|
||||
},
|
||||
},
|
||||
@@ -58,7 +58,7 @@ func TestEvaluateExecutionResult(t *testing.T) {
|
||||
{
|
||||
desc: "nil value single instance is single a NoData state result",
|
||||
execResults: ExecutionResults{
|
||||
Results: []*data.Frame{
|
||||
Condition: []*data.Frame{
|
||||
data.NewFrame("", data.NewField("", nil, []*float64{nil})),
|
||||
},
|
||||
},
|
||||
@@ -95,7 +95,7 @@ func TestEvaluateExecutionResult(t *testing.T) {
|
||||
{
|
||||
desc: "frame with no fields produces a NoData state result",
|
||||
execResults: ExecutionResults{
|
||||
Results: []*data.Frame{
|
||||
Condition: []*data.Frame{
|
||||
data.NewFrame(""),
|
||||
},
|
||||
},
|
||||
@@ -109,7 +109,7 @@ func TestEvaluateExecutionResult(t *testing.T) {
|
||||
{
|
||||
desc: "empty field produces a NoData state result",
|
||||
execResults: ExecutionResults{
|
||||
Results: []*data.Frame{
|
||||
Condition: []*data.Frame{
|
||||
data.NewFrame("", data.NewField("", nil, []*float64{})),
|
||||
},
|
||||
},
|
||||
@@ -123,7 +123,7 @@ func TestEvaluateExecutionResult(t *testing.T) {
|
||||
{
|
||||
desc: "empty field with labels produces a NoData state result with labels",
|
||||
execResults: ExecutionResults{
|
||||
Results: []*data.Frame{
|
||||
Condition: []*data.Frame{
|
||||
data.NewFrame("", data.NewField("", data.Labels{"a": "b"}, []*float64{})),
|
||||
},
|
||||
},
|
||||
@@ -138,7 +138,7 @@ func TestEvaluateExecutionResult(t *testing.T) {
|
||||
{
|
||||
desc: "malformed frame (unequal lengths) produces Error state result",
|
||||
execResults: ExecutionResults{
|
||||
Results: []*data.Frame{
|
||||
Condition: []*data.Frame{
|
||||
data.NewFrame("",
|
||||
data.NewField("", nil, []*float64{ptr.Float64(23)}),
|
||||
data.NewField("", nil, []*float64{}),
|
||||
@@ -156,7 +156,7 @@ func TestEvaluateExecutionResult(t *testing.T) {
|
||||
{
|
||||
desc: "too many fields produces Error state result",
|
||||
execResults: ExecutionResults{
|
||||
Results: []*data.Frame{
|
||||
Condition: []*data.Frame{
|
||||
data.NewFrame("",
|
||||
data.NewField("", nil, []*float64{}),
|
||||
data.NewField("", nil, []*float64{}),
|
||||
@@ -174,7 +174,7 @@ func TestEvaluateExecutionResult(t *testing.T) {
|
||||
{
|
||||
desc: "more than one row produces Error state result",
|
||||
execResults: ExecutionResults{
|
||||
Results: []*data.Frame{
|
||||
Condition: []*data.Frame{
|
||||
data.NewFrame("",
|
||||
data.NewField("", nil, []*float64{ptr.Float64(2), ptr.Float64(3)}),
|
||||
),
|
||||
@@ -191,7 +191,7 @@ func TestEvaluateExecutionResult(t *testing.T) {
|
||||
{
|
||||
desc: "time fields (looks like time series) returns error",
|
||||
execResults: ExecutionResults{
|
||||
Results: []*data.Frame{
|
||||
Condition: []*data.Frame{
|
||||
data.NewFrame("",
|
||||
data.NewField("", nil, []time.Time{}),
|
||||
),
|
||||
@@ -208,7 +208,7 @@ func TestEvaluateExecutionResult(t *testing.T) {
|
||||
{
|
||||
desc: "non []*float64 field will produce Error state result",
|
||||
execResults: ExecutionResults{
|
||||
Results: []*data.Frame{
|
||||
Condition: []*data.Frame{
|
||||
data.NewFrame("",
|
||||
data.NewField("", nil, []float64{2}),
|
||||
),
|
||||
@@ -225,7 +225,7 @@ func TestEvaluateExecutionResult(t *testing.T) {
|
||||
{
|
||||
desc: "duplicate labels produce a single Error state result",
|
||||
execResults: ExecutionResults{
|
||||
Results: []*data.Frame{
|
||||
Condition: []*data.Frame{
|
||||
data.NewFrame("",
|
||||
data.NewField("", nil, []*float64{ptr.Float64(1)}),
|
||||
),
|
||||
@@ -245,7 +245,7 @@ func TestEvaluateExecutionResult(t *testing.T) {
|
||||
{
|
||||
desc: "error that produce duplicate empty labels produce a single Error state result",
|
||||
execResults: ExecutionResults{
|
||||
Results: []*data.Frame{
|
||||
Condition: []*data.Frame{
|
||||
data.NewFrame("",
|
||||
data.NewField("", data.Labels{"a": "b"}, []float64{2}),
|
||||
),
|
||||
@@ -265,7 +265,7 @@ func TestEvaluateExecutionResult(t *testing.T) {
|
||||
{
|
||||
desc: "certain errors will produce multiple mixed Error and other state results",
|
||||
execResults: ExecutionResults{
|
||||
Results: []*data.Frame{
|
||||
Condition: []*data.Frame{
|
||||
data.NewFrame("",
|
||||
data.NewField("", nil, []float64{3}),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user