@ -8,6 +8,7 @@ import (
"sort"
"time"
"github.com/grafana/grafana/pkg/expr/classic"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/setting"
@ -146,6 +147,12 @@ func GetExprRequest(ctx AlertExecCtx, data []models.AlertQuery, now time.Time) (
return req , nil
}
type NumberValueCapture struct {
Var string // RefID
Labels data . Labels
Value * float64
}
func executeCondition ( ctx AlertExecCtx , c * models . Condition , now time . Time , dataService * tsdb . Service ) ExecutionResults {
result := ExecutionResults { }
@ -155,11 +162,59 @@ func executeCondition(ctx AlertExecCtx, c *models.Condition, now time.Time, data
return ExecutionResults { Error : err }
}
// eval captures for the '__value__' label.
captures := make ( [ ] NumberValueCapture , 0 , len ( execResp . Responses ) )
captureVal := func ( refID string , labels data . Labels , value * float64 ) {
captures = append ( captures , NumberValueCapture {
Var : refID ,
Value : value ,
Labels : labels . Copy ( ) ,
} )
}
for refID , res := range execResp . Responses {
if refID != c . Condition {
continue
// 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 {
if len ( frame . Fields ) != 1 || frame . Fields [ 0 ] . Type ( ) != data . FieldTypeNullableFloat64 {
continue
}
var v * float64
if frame . Fields [ 0 ] . Len ( ) == 1 {
v = frame . At ( 0 , 0 ) . ( * float64 ) // type checked above
}
captureVal ( frame . RefID , frame . Fields [ 0 ] . Labels , v )
}
if refID == c . Condition {
result . Results = res . Frames
}
}
// add capture values as data frame metadata to each result (frame) that has matching labels.
for _ , frame := range result . Results {
// 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 {
continue // do not overwrite EvalMatch from classic condition.
}
}
frame . SetMeta ( & data . FrameMeta { } ) // overwrite metadata
if len ( frame . Fields ) == 1 {
theseLabels := frame . Fields [ 0 ] . Labels
for _ , cap := range captures {
// matching labels are equal labels, or when one set of labels includes the labels of the other.
if theseLabels . Equals ( cap . Labels ) || theseLabels . Contains ( cap . Labels ) || cap . Labels . Contains ( theseLabels ) {
if frame . Meta . Custom == nil {
frame . Meta . Custom = [ ] NumberValueCapture { }
}
frame . Meta . Custom = append ( frame . Meta . Custom . ( [ ] NumberValueCapture ) , cap )
}
}
}
result . Results = res . Frames
}
return result