Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pkg/ottl] Update Statements to implement filter.BoolExpr #18915

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .chloggen/ottl-add-eval-to-statements.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: pkg/ottl

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add Eval function to Statements

# One or more tracking issues related to the change
issues: [18915]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
22 changes: 22 additions & 0 deletions pkg/ottl/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,25 @@ func (s *Statements[K]) Execute(ctx context.Context, tCtx K) error {
}
return nil
}

// Eval returns true if any statement's condition is true and returns false otherwise.
// Does not execute the statement's function.
// When errorMode is `propagate`, errors cause the evaluation to be false and an error is returned.
// When errorMode is `ignore`, errors cause evaluation to continue to the next statement.
func (s *Statements[K]) Eval(ctx context.Context, tCtx K) (bool, error) {
for _, statement := range s.statements {
match, err := statement.condition.Eval(ctx, tCtx)
if err != nil {
if s.errorMode == PropagateError {
err = fmt.Errorf("failed to eval statement: %v, %w", statement.origText, err)
return false, err
}
s.telemetrySettings.Logger.Warn("failed to eval statement", zap.Error(err), zap.String("statement", statement.origText))
continue
}
if match {
return true, nil
}
}
return false, nil
}
121 changes: 120 additions & 1 deletion pkg/ottl/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1312,7 +1312,7 @@ func Test_Execute(t *testing.T) {
}
}

func Test_Execute_Error(t *testing.T) {
func Test_Statements_Execute_Error(t *testing.T) {
tests := []struct {
name string
condition boolExpressionEvaluator[interface{}]
Expand Down Expand Up @@ -1382,3 +1382,122 @@ func Test_Execute_Error(t *testing.T) {
})
}
}

func Test_Statements_Eval(t *testing.T) {
tests := []struct {
name string
conditions []boolExpressionEvaluator[interface{}]
function ExprFunc[interface{}]
errorMode ErrorMode
expectedResult bool
}{
{
name: "True",
conditions: []boolExpressionEvaluator[interface{}]{
alwaysTrue[interface{}],
},
errorMode: IgnoreError,
expectedResult: true,
},
{
name: "At least one True",
conditions: []boolExpressionEvaluator[interface{}]{
alwaysFalse[interface{}],
alwaysFalse[interface{}],
alwaysTrue[interface{}],
},
errorMode: IgnoreError,
expectedResult: true,
},
{
name: "False",
conditions: []boolExpressionEvaluator[interface{}]{
alwaysFalse[interface{}],
alwaysFalse[interface{}],
},
errorMode: IgnoreError,
expectedResult: false,
},
{
name: "Error is false when using Ignore",
conditions: []boolExpressionEvaluator[interface{}]{
alwaysFalse[interface{}],
func(context.Context, interface{}) (bool, error) {
return true, fmt.Errorf("test")
},
alwaysTrue[interface{}],
},
errorMode: IgnoreError,
expectedResult: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var rawStatements []*Statement[interface{}]
for _, condition := range tt.conditions {
rawStatements = append(rawStatements, &Statement[interface{}]{
condition: BoolExpr[any]{condition},
function: Expr[any]{
exprFunc: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
return nil, fmt.Errorf("function should not be called")
},
},
})
}

statements := Statements[interface{}]{
statements: rawStatements,
telemetrySettings: componenttest.NewNopTelemetrySettings(),
errorMode: tt.errorMode,
}

result, err := statements.Eval(context.Background(), nil)
assert.NoError(t, err)
assert.Equal(t, tt.expectedResult, result)
})
}
}

func Test_Statements_Eval_Error(t *testing.T) {
tests := []struct {
name string
conditions []boolExpressionEvaluator[interface{}]
function ExprFunc[interface{}]
errorMode ErrorMode
}{
{
name: "Propagate Error from function",
conditions: []boolExpressionEvaluator[interface{}]{
func(context.Context, interface{}) (bool, error) {
return true, fmt.Errorf("test")
},
},
errorMode: PropagateError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var rawStatements []*Statement[interface{}]
for _, condition := range tt.conditions {
rawStatements = append(rawStatements, &Statement[interface{}]{
condition: BoolExpr[any]{condition},
function: Expr[any]{
exprFunc: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
return nil, fmt.Errorf("function should not be called")
},
},
})
}

statements := Statements[interface{}]{
statements: rawStatements,
telemetrySettings: componenttest.NewNopTelemetrySettings(),
errorMode: tt.errorMode,
}

result, err := statements.Eval(context.Background(), nil)
assert.Error(t, err)
assert.False(t, result)
})
}
}
32 changes: 10 additions & 22 deletions processor/filterprocessor/internal/common/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ func ParseSpan(conditions []string, set component.TelemetrySettings) (expr.BoolE
if err != nil {
return nil, err
}
return statementsToExpr(statements), nil
s := ottlspan.NewStatements(statements, set, ottlspan.WithErrorMode(ottl.PropagateError))
return &s, nil
}

func ParseSpanEvent(conditions []string, set component.TelemetrySettings) (expr.BoolExpr[ottlspanevent.TransformContext], error) {
Expand All @@ -54,7 +55,8 @@ func ParseSpanEvent(conditions []string, set component.TelemetrySettings) (expr.
if err != nil {
return nil, err
}
return statementsToExpr(statements), nil
s := ottlspanevent.NewStatements(statements, set, ottlspanevent.WithErrorMode(ottl.PropagateError))
return &s, nil
}

func ParseLog(conditions []string, set component.TelemetrySettings) (expr.BoolExpr[ottllog.TransformContext], error) {
Expand All @@ -67,7 +69,8 @@ func ParseLog(conditions []string, set component.TelemetrySettings) (expr.BoolEx
if err != nil {
return nil, err
}
return statementsToExpr(statements), nil
s := ottllog.NewStatements(statements, set, ottllog.WithErrorMode(ottl.PropagateError))
return &s, nil
}

func ParseMetric(conditions []string, set component.TelemetrySettings) (expr.BoolExpr[ottlmetric.TransformContext], error) {
Expand All @@ -80,7 +83,8 @@ func ParseMetric(conditions []string, set component.TelemetrySettings) (expr.Boo
if err != nil {
return nil, err
}
return statementsToExpr(statements), nil
s := ottlmetric.NewStatements(statements, set, ottlmetric.WithErrorMode(ottl.PropagateError))
return &s, nil
}

func ParseDataPoint(conditions []string, set component.TelemetrySettings) (expr.BoolExpr[ottldatapoint.TransformContext], error) {
Expand All @@ -93,7 +97,8 @@ func ParseDataPoint(conditions []string, set component.TelemetrySettings) (expr.
if err != nil {
return nil, err
}
return statementsToExpr(statements), nil
s := ottldatapoint.NewStatements(statements, set, ottldatapoint.WithErrorMode(ottl.PropagateError))
return &s, nil
}

func conditionsToStatements(conditions []string) []string {
Expand All @@ -104,23 +109,6 @@ func conditionsToStatements(conditions []string) []string {
return statements
}

type statementExpr[K any] struct {
statement *ottl.Statement[K]
}

func (se statementExpr[K]) Eval(ctx context.Context, tCtx K) (bool, error) {
_, ret, err := se.statement.Execute(ctx, tCtx)
return ret, err
}

func statementsToExpr[K any](statements []*ottl.Statement[K]) expr.BoolExpr[K] {
var rets []expr.BoolExpr[K]
for _, statement := range statements {
rets = append(rets, statementExpr[K]{statement: statement})
}
return expr.Or(rets...)
}

func functions[K any]() map[string]interface{} {
return map[string]interface{}{
"TraceID": ottlfuncs.TraceID[K],
Expand Down