Alerting: Set "value" with evalmatches in G Managed (#34075)
When, and currently only when using a classic condition, evaluation information is added (which is like the EvalMatches from dashboard alerting). This is returned via the API and can be included in notifications by reading the `__value__` label attached `.Alerts` in the template. It is a string.
This commit is contained in:
@@ -63,6 +63,11 @@ type Result struct {
|
||||
Error error
|
||||
EvaluatedAt time.Time
|
||||
EvaluationDuration time.Duration
|
||||
|
||||
// EvaluationSring 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.
|
||||
@@ -265,6 +270,7 @@ func evaluateExecutionResult(execResults ExecutionResults, ts time.Time) Results
|
||||
Instance: f.Fields[0].Labels,
|
||||
EvaluatedAt: ts,
|
||||
EvaluationDuration: time.Since(ts),
|
||||
EvaluationString: extractEvalString(f),
|
||||
}
|
||||
|
||||
switch {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package eval
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/expr/classic"
|
||||
)
|
||||
|
||||
func extractEvalString(frame *data.Frame) (s string) {
|
||||
if frame == nil {
|
||||
return "empty frame"
|
||||
}
|
||||
|
||||
if frame.Meta == nil || frame.Meta.Custom == nil {
|
||||
return
|
||||
}
|
||||
|
||||
evalMatches, ok := frame.Meta.Custom.([]classic.EvalMatch)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
sb := strings.Builder{}
|
||||
|
||||
for i, m := range evalMatches {
|
||||
sb.WriteString("[ ")
|
||||
sb.WriteString(fmt.Sprintf("metric='%s' ", m.Metric))
|
||||
sb.WriteString(fmt.Sprintf("labels={%s} ", m.Labels))
|
||||
|
||||
valString := "null"
|
||||
if m.Value != nil {
|
||||
valString = fmt.Sprintf("%v", *m.Value)
|
||||
}
|
||||
|
||||
sb.WriteString(fmt.Sprintf("value=%v ", valString))
|
||||
|
||||
sb.WriteString("]")
|
||||
if i < len(evalMatches)-1 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package eval
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/expr/classic"
|
||||
"github.com/stretchr/testify/require"
|
||||
ptr "github.com/xorcare/pointer"
|
||||
)
|
||||
|
||||
func TestExtractEvalString(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
inFrame *data.Frame
|
||||
outString string
|
||||
}{
|
||||
{
|
||||
desc: "1 EvalMatch",
|
||||
inFrame: newMetaFrame([]classic.EvalMatch{
|
||||
{Metric: "Test", Labels: data.Labels{"host": "foo"}, Value: ptr.Float64(32.3)},
|
||||
}, ptr.Float64(1)),
|
||||
outString: `[ metric='Test' labels={host=foo} value=32.3 ]`,
|
||||
},
|
||||
{
|
||||
desc: "2 EvalMatches",
|
||||
inFrame: newMetaFrame([]classic.EvalMatch{
|
||||
{Metric: "Test", Labels: data.Labels{"host": "foo"}, Value: ptr.Float64(32.3)},
|
||||
{Metric: "Test", Labels: data.Labels{"host": "baz"}, Value: ptr.Float64(10)},
|
||||
}, ptr.Float64(1)),
|
||||
outString: `[ metric='Test' labels={host=foo} value=32.3 ], [ metric='Test' labels={host=baz} value=10 ]`,
|
||||
},
|
||||
{
|
||||
desc: "3 EvalMatches",
|
||||
inFrame: newMetaFrame([]classic.EvalMatch{
|
||||
{Metric: "Test", Labels: data.Labels{"host": "foo"}, Value: ptr.Float64(32.3)},
|
||||
{Metric: "Test", Labels: data.Labels{"host": "baz"}, Value: ptr.Float64(10)},
|
||||
{Metric: "TestA", Labels: data.Labels{"host": "zip"}, Value: ptr.Float64(11)},
|
||||
}, ptr.Float64(1)),
|
||||
outString: `[ metric='Test' labels={host=foo} value=32.3 ], [ metric='Test' labels={host=baz} value=10 ], [ metric='TestA' labels={host=zip} value=11 ]`,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
require.Equal(t, tc.outString, extractEvalString(tc.inFrame))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newMetaFrame(custom interface{}, val *float64) *data.Frame {
|
||||
return data.NewFrame("",
|
||||
data.NewField("", nil, []*float64{val})).
|
||||
SetMeta(&data.FrameMeta{
|
||||
Custom: custom,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user