Alerting: Log expression command types during evaluation (#84614)

This commit is contained in:
Yuri Tseretyan
2024-03-19 10:00:03 -04:00
committed by GitHub
parent 09817e2c7f
commit 9dc4221508
11 changed files with 101 additions and 0 deletions

View File

@ -216,6 +216,10 @@ func (cmd *ConditionsCmd) executeCond(_ context.Context, _ time.Time, cond condi
return isCondFiring, isCondNoData, matches, nil return isCondFiring, isCondNoData, matches, nil
} }
func (cmd *ConditionsCmd) Type() string {
return "classic_condition"
}
func compareWithOperator(b1, b2 bool, operator ConditionOperatorType) bool { func compareWithOperator(b1, b2 bool, operator ConditionOperatorType) bool {
if operator == "or" { if operator == "or" {
return b1 || b2 return b1 || b2

View File

@ -19,6 +19,7 @@ import (
type Command interface { type Command interface {
NeedsVars() []string NeedsVars() []string
Execute(ctx context.Context, now time.Time, vars mathexp.Vars, tracer tracing.Tracer) (mathexp.Results, error) Execute(ctx context.Context, now time.Time, vars mathexp.Vars, tracer tracing.Tracer) (mathexp.Results, error)
Type() string
} }
// MathCommand is a command for a math expression such as "1 + $GA / 2" // MathCommand is a command for a math expression such as "1 + $GA / 2"
@ -75,6 +76,10 @@ func (gm *MathCommand) Execute(ctx context.Context, _ time.Time, vars mathexp.Va
return gm.Expression.Execute(gm.refID, vars, tracer) return gm.Expression.Execute(gm.refID, vars, tracer)
} }
func (gm *MathCommand) Type() string {
return TypeMath.String()
}
// ReduceCommand is an expression command for reduction of a timeseries such as a min, mean, or max. // ReduceCommand is an expression command for reduction of a timeseries such as a min, mean, or max.
type ReduceCommand struct { type ReduceCommand struct {
Reducer mathexp.ReducerID Reducer mathexp.ReducerID
@ -201,6 +206,10 @@ func (gr *ReduceCommand) Execute(ctx context.Context, _ time.Time, vars mathexp.
return newRes, nil return newRes, nil
} }
func (gr *ReduceCommand) Type() string {
return TypeReduce.String()
}
// ResampleCommand is an expression command for resampling of a timeseries. // ResampleCommand is an expression command for resampling of a timeseries.
type ResampleCommand struct { type ResampleCommand struct {
Window time.Duration Window time.Duration
@ -312,6 +321,10 @@ func (gr *ResampleCommand) Execute(ctx context.Context, now time.Time, vars math
return newRes, nil return newRes, nil
} }
func (gr *ResampleCommand) Type() string {
return TypeResample.String()
}
// CommandType is the type of the expression command. // CommandType is the type of the expression command.
type CommandType int type CommandType int
@ -342,6 +355,8 @@ func (gt CommandType) String() string {
return "resample" return "resample"
case TypeClassicConditions: case TypeClassicConditions:
return "classic_conditions" return "classic_conditions"
case TypeThreshold:
return "threshold"
case TypeSQL: case TypeSQL:
return "sql" return "sql"
default: default:

View File

@ -4,9 +4,11 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"slices"
"time" "time"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"golang.org/x/exp/maps"
"gonum.org/v1/gonum/graph/simple" "gonum.org/v1/gonum/graph/simple"
"gonum.org/v1/gonum/graph/topo" "gonum.org/v1/gonum/graph/topo"
@ -123,6 +125,56 @@ func (dp *DataPipeline) execute(c context.Context, now time.Time, s *Service) (m
return vars, nil return vars, nil
} }
// GetDatasourceTypes returns an unique list of data source types used in the query. Machine learning node is encoded as `ml_<type>`, e.g. ml_outlier
func (dp *DataPipeline) GetDatasourceTypes() []string {
if dp == nil {
return nil
}
m := make(map[string]struct{}, 2)
for _, node := range *dp {
name := ""
switch t := node.(type) {
case *DSNode:
if t.datasource != nil {
name = t.datasource.Type
}
case *MLNode:
name = fmt.Sprintf("ml_%s", t.command.Type())
}
if name == "" {
continue
}
m[name] = struct{}{}
}
result := maps.Keys(m)
slices.Sort(result)
return result
}
// GetCommandTypes returns a sorted unique list of all server-side expression commands used in the pipeline.
func (dp *DataPipeline) GetCommandTypes() []string {
if dp == nil {
return nil
}
m := make(map[string]struct{}, 5) // 5 is big enough to cover most of the cases
for _, node := range *dp {
name := ""
switch t := node.(type) {
case *CMDNode:
if t.Command != nil {
name = t.Command.Type()
}
}
if name == "" {
continue
}
m[name] = struct{}{}
}
result := maps.Keys(m)
slices.Sort(result)
return result
}
// BuildPipeline builds a graph of the nodes, and returns the nodes in an // BuildPipeline builds a graph of the nodes, and returns the nodes in an
// executable order. // executable order.
func (s *Service) buildPipeline(req *Request) (DataPipeline, error) { func (s *Service) buildPipeline(req *Request) (DataPipeline, error) {

View File

@ -77,6 +77,10 @@ func (h *HysteresisCommand) Execute(ctx context.Context, now time.Time, vars mat
return mathexp.Results{Values: append(loadingResults.Values, unloadingResults.Values...)}, nil return mathexp.Results{Values: append(loadingResults.Values, unloadingResults.Values...)}, nil
} }
func (h HysteresisCommand) Type() string {
return "hysteresis"
}
func NewHysteresisCommand(refID string, referenceVar string, loadCondition ThresholdCommand, unloadCondition ThresholdCommand, l Fingerprints) (*HysteresisCommand, error) { func NewHysteresisCommand(refID string, referenceVar string, loadCondition ThresholdCommand, unloadCondition ThresholdCommand, l Fingerprints) (*HysteresisCommand, error) {
return &HysteresisCommand{ return &HysteresisCommand{
RefID: refID, RefID: refID,

View File

@ -31,6 +31,8 @@ type Command interface {
// Execute creates a payload send request to the ML API by calling the function argument sendRequest, and then parses response. // Execute creates a payload send request to the ML API by calling the function argument sendRequest, and then parses response.
// Function sendRequest is supposed to abstract the client configuration such creating http request, adding authorization parameters, host etc. // Function sendRequest is supposed to abstract the client configuration such creating http request, adding authorization parameters, host etc.
Execute(from, to time.Time, sendRequest func(method string, path string, payload []byte) (response.Response, error)) (*backend.QueryDataResponse, error) Execute(from, to time.Time, sendRequest func(method string, path string, payload []byte) (response.Response, error)) (*backend.QueryDataResponse, error)
Type() string
} }
// UnmarshalCommand parses a config parameters and creates a command. Requires key `type` to be specified. // UnmarshalCommand parses a config parameters and creates a command. Requires key `type` to be specified.

View File

@ -19,6 +19,10 @@ type OutlierCommand struct {
var _ Command = OutlierCommand{} var _ Command = OutlierCommand{}
func (c OutlierCommand) Type() string {
return "outlier"
}
func (c OutlierCommand) DatasourceUID() string { func (c OutlierCommand) DatasourceUID() string {
return c.config.DatasourceUID return c.config.DatasourceUID
} }

View File

@ -42,3 +42,7 @@ func (f *FakeCommand) Execute(from, to time.Time, executor func(method string, p
} }
return f.Response, f.Error return f.Response, f.Error
} }
func (f *FakeCommand) Type() string {
return "fake"
}

View File

@ -187,6 +187,13 @@ type DSNode struct {
request Request request Request
} }
func (dn *DSNode) String() string {
if dn.datasource == nil {
return "unknown"
}
return dn.datasource.Type
}
// NodeType returns the data pipeline node type. // NodeType returns the data pipeline node type.
func (dn *DSNode) NodeType() NodeType { func (dn *DSNode) NodeType() NodeType {
return TypeDatasourceNode return TypeDatasourceNode

View File

@ -103,3 +103,7 @@ func (gr *SQLCommand) Execute(ctx context.Context, now time.Time, vars mathexp.V
return rsp, nil return rsp, nil
} }
func (gr *SQLCommand) Type() string {
return TypeSQL.String()
}

View File

@ -128,6 +128,10 @@ func (tc *ThresholdCommand) Execute(ctx context.Context, now time.Time, vars mat
return mathCommand.Execute(ctx, now, vars, tracer) return mathCommand.Execute(ctx, now, vars, tracer)
} }
func (tc *ThresholdCommand) Type() string {
return TypeThreshold.String()
}
// createMathExpression converts all the info we have about a "threshold" expression in to a Math expression // createMathExpression converts all the info we have about a "threshold" expression in to a Math expression
func createMathExpression(referenceVar string, thresholdFunc ThresholdType, args []float64, invert bool) (string, error) { func createMathExpression(referenceVar string, thresholdFunc ThresholdType, args []float64, invert bool) (string, error) {
var exp string var exp string

View File

@ -73,6 +73,7 @@ func (r *conditionEvaluator) EvaluateRaw(ctx context.Context, now time.Time) (re
defer cancel() defer cancel()
execCtx = timeoutCtx execCtx = timeoutCtx
} }
logger.FromContext(ctx).Debug("Executing pipeline", "commands", strings.Join(r.pipeline.GetCommandTypes(), ","), "datasources", strings.Join(r.pipeline.GetDatasourceTypes(), ","))
return r.expressionService.ExecutePipeline(execCtx, now, r.pipeline) return r.expressionService.ExecutePipeline(execCtx, now, r.pipeline)
} }