Skip to content

Commit

Permalink
[pkg/ottl] Add ConditionSequence struct (#29339)
Browse files Browse the repository at this point in the history
**Description:** 

Adds a new `ConditionSequence` struct to help handle processing a list
of Conditions. The primary reason to use a `ConditionSequence` is to let
the struct handle errors. Since that is its defining purpose, I opted to
make `ErrorMode` a required argument in the "constructor" instead of an
Option.

I also update `internal/filterottl` to use this struct instead of
`Statements`. This is a non-breaking change.

If we like this pattern, I will do a breaking change to replace
`Statements` with a similar `StatementSequence` struct in a future PR .

See these structs implemented in components here:
#29294

**Link to tracking Issue:** <Issue number if applicable>

Related to
#13545

**Testing:**

Added new tests

**Documentation:** <Describe the documentation added.>
Added godoc comments

---------

Co-authored-by: Evan Bradley <[email protected]>
  • Loading branch information
TylerHelmuth and evan-bradley authored Nov 30, 2023
1 parent 16760c7 commit 95a77c1
Show file tree
Hide file tree
Showing 13 changed files with 445 additions and 88 deletions.
27 changes: 27 additions & 0 deletions .chloggen/ottl-condition-sequence.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

# 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 `ConditionSequence` for evaluating lists of conditions

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [29339]

# (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:

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [api]
10 changes: 5 additions & 5 deletions connector/countconnector/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ func TestConfigErrors(t *testing.T) {
},
},
},
expect: fmt.Sprintf("spans condition: metric %q: unable to parse OTTL statement", defaultMetricNameSpans),
expect: fmt.Sprintf("spans condition: metric %q: unable to parse OTTL condition", defaultMetricNameSpans),
},
{
name: "invalid_condition_spanevent",
Expand All @@ -425,7 +425,7 @@ func TestConfigErrors(t *testing.T) {
},
},
},
expect: fmt.Sprintf("spanevents condition: metric %q: unable to parse OTTL statement", defaultMetricNameSpanEvents),
expect: fmt.Sprintf("spanevents condition: metric %q: unable to parse OTTL condition", defaultMetricNameSpanEvents),
},
{
name: "invalid_condition_metric",
Expand All @@ -437,7 +437,7 @@ func TestConfigErrors(t *testing.T) {
},
},
},
expect: fmt.Sprintf("metrics condition: metric %q: unable to parse OTTL statement", defaultMetricNameMetrics),
expect: fmt.Sprintf("metrics condition: metric %q: unable to parse OTTL condition", defaultMetricNameMetrics),
},
{
name: "invalid_condition_datapoint",
Expand All @@ -449,7 +449,7 @@ func TestConfigErrors(t *testing.T) {
},
},
},
expect: fmt.Sprintf("datapoints condition: metric %q: unable to parse OTTL statement", defaultMetricNameDataPoints),
expect: fmt.Sprintf("datapoints condition: metric %q: unable to parse OTTL condition", defaultMetricNameDataPoints),
},
{
name: "invalid_condition_log",
Expand All @@ -461,7 +461,7 @@ func TestConfigErrors(t *testing.T) {
},
},
},
expect: fmt.Sprintf("logs condition: metric %q: unable to parse OTTL statement", defaultMetricNameLogs),
expect: fmt.Sprintf("logs condition: metric %q: unable to parse OTTL condition", defaultMetricNameLogs),
},
}

Expand Down
74 changes: 18 additions & 56 deletions internal/filter/filterottl/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,132 +20,94 @@ import (
// The passed in functions should use the ottlspan.TransformContext.
// If a function named `match` is not present in the function map it will be added automatically so that parsing works as expected
func NewBoolExprForSpan(conditions []string, functions map[string]ottl.Factory[ottlspan.TransformContext], errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottlspan.TransformContext], error) {
match := newMatchFactory[ottlspan.TransformContext]()
if _, ok := functions[match.Name()]; !ok {
functions[match.Name()] = match
}
statmentsStr := conditionsToStatements(conditions)
parser, err := ottlspan.NewParser(functions, set)
if err != nil {
return nil, err
}
statements, err := parser.ParseStatements(statmentsStr)
statements, err := parser.ParseConditions(conditions)
if err != nil {
return nil, err
}
s := ottlspan.NewStatements(statements, set, ottlspan.WithErrorMode(errorMode))
return &s, nil
c := ottlspan.NewConditionSequence(statements, set, ottlspan.WithConditionSequenceErrorMode(errorMode))
return &c, nil
}

// NewBoolExprForSpanEvent creates a BoolExpr[ottlspanevent.TransformContext] that will return true if any of the given OTTL conditions evaluate to true.
// The passed in functions should use the ottlspanevent.TransformContext.
// If a function named `match` is not present in the function map it will be added automatically so that parsing works as expected
func NewBoolExprForSpanEvent(conditions []string, functions map[string]ottl.Factory[ottlspanevent.TransformContext], errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottlspanevent.TransformContext], error) {
match := newMatchFactory[ottlspanevent.TransformContext]()
if _, ok := functions[match.Name()]; !ok {
functions[match.Name()] = match
}
statmentsStr := conditionsToStatements(conditions)
parser, err := ottlspanevent.NewParser(functions, set)
if err != nil {
return nil, err
}
statements, err := parser.ParseStatements(statmentsStr)
statements, err := parser.ParseConditions(conditions)
if err != nil {
return nil, err
}
s := ottlspanevent.NewStatements(statements, set, ottlspanevent.WithErrorMode(errorMode))
return &s, nil
c := ottlspanevent.NewConditionSequence(statements, set, ottlspanevent.WithConditionSequenceErrorMode(errorMode))
return &c, nil
}

// NewBoolExprForMetric creates a BoolExpr[ottlmetric.TransformContext] that will return true if any of the given OTTL conditions evaluate to true.
// The passed in functions should use the ottlmetric.TransformContext.
// If a function named `match` is not present in the function map it will be added automatically so that parsing works as expected
func NewBoolExprForMetric(conditions []string, functions map[string]ottl.Factory[ottlmetric.TransformContext], errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottlmetric.TransformContext], error) {
match := newMatchFactory[ottlmetric.TransformContext]()
if _, ok := functions[match.Name()]; !ok {
functions[match.Name()] = match
}
statmentsStr := conditionsToStatements(conditions)
parser, err := ottlmetric.NewParser(functions, set)
if err != nil {
return nil, err
}
statements, err := parser.ParseStatements(statmentsStr)
statements, err := parser.ParseConditions(conditions)
if err != nil {
return nil, err
}
s := ottlmetric.NewStatements(statements, set, ottlmetric.WithErrorMode(errorMode))
return &s, nil
c := ottlmetric.NewConditionSequence(statements, set, ottlmetric.WithConditionSequenceErrorMode(errorMode))
return &c, nil
}

// NewBoolExprForDataPoint creates a BoolExpr[ottldatapoint.TransformContext] that will return true if any of the given OTTL conditions evaluate to true.
// The passed in functions should use the ottldatapoint.TransformContext.
// If a function named `match` is not present in the function map it will be added automatically so that parsing works as expected
func NewBoolExprForDataPoint(conditions []string, functions map[string]ottl.Factory[ottldatapoint.TransformContext], errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottldatapoint.TransformContext], error) {
match := newMatchFactory[ottldatapoint.TransformContext]()
if _, ok := functions[match.Name()]; !ok {
functions[match.Name()] = match
}
statmentsStr := conditionsToStatements(conditions)
parser, err := ottldatapoint.NewParser(functions, set)
if err != nil {
return nil, err
}
statements, err := parser.ParseStatements(statmentsStr)
statements, err := parser.ParseConditions(conditions)
if err != nil {
return nil, err
}
s := ottldatapoint.NewStatements(statements, set, ottldatapoint.WithErrorMode(errorMode))
return &s, nil
c := ottldatapoint.NewConditionSequence(statements, set, ottldatapoint.WithConditionSequenceErrorMode(errorMode))
return &c, nil
}

// NewBoolExprForLog creates a BoolExpr[ottllog.TransformContext] that will return true if any of the given OTTL conditions evaluate to true.
// The passed in functions should use the ottllog.TransformContext.
// If a function named `match` is not present in the function map it will be added automatically so that parsing works as expected
func NewBoolExprForLog(conditions []string, functions map[string]ottl.Factory[ottllog.TransformContext], errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottllog.TransformContext], error) {
match := newMatchFactory[ottllog.TransformContext]()
if _, ok := functions[match.Name()]; !ok {
functions[match.Name()] = match
}
statmentsStr := conditionsToStatements(conditions)
parser, err := ottllog.NewParser(functions, set)
if err != nil {
return nil, err
}
statements, err := parser.ParseStatements(statmentsStr)
statements, err := parser.ParseConditions(conditions)
if err != nil {
return nil, err
}
s := ottllog.NewStatements(statements, set, ottllog.WithErrorMode(errorMode))
return &s, nil
c := ottllog.NewConditionSequence(statements, set, ottllog.WithConditionSequenceErrorMode(errorMode))
return &c, nil
}

// NewBoolExprForResource creates a BoolExpr[ottlresource.TransformContext] that will return true if any of the given OTTL conditions evaluate to true.
// The passed in functions should use the ottlresource.TransformContext.
// If a function named `match` is not present in the function map it will be added automatically so that parsing works as expected
func NewBoolExprForResource(conditions []string, functions map[string]ottl.Factory[ottlresource.TransformContext], errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottlresource.TransformContext], error) {
match := newMatchFactory[ottlresource.TransformContext]()
if _, ok := functions[match.Name()]; !ok {
functions[match.Name()] = match
}
statmentsStr := conditionsToStatements(conditions)
parser, err := ottlresource.NewParser(functions, set)
if err != nil {
return nil, err
}
statements, err := parser.ParseStatements(statmentsStr)
statements, err := parser.ParseConditions(conditions)
if err != nil {
return nil, err
}
s := ottlresource.NewStatements(statements, set, ottlresource.WithErrorMode(errorMode))
return &s, nil
}

func conditionsToStatements(conditions []string) []string {
statements := make([]string, len(conditions))
for i, condition := range conditions {
statements[i] = "match() where " + condition
}
return statements
c := ottlresource.NewConditionSequence(statements, set, ottlresource.WithConditionSequenceErrorMode(errorMode))
return &c, nil
}
33 changes: 6 additions & 27 deletions internal/filter/filterottl/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ import (
)

func StandardSpanFuncs() map[string]ottl.Factory[ottlspan.TransformContext] {
return standardFuncs[ottlspan.TransformContext]()
return ottlfuncs.StandardConverters[ottlspan.TransformContext]()
}

func StandardSpanEventFuncs() map[string]ottl.Factory[ottlspanevent.TransformContext] {
return standardFuncs[ottlspanevent.TransformContext]()
return ottlfuncs.StandardConverters[ottlspanevent.TransformContext]()
}

func StandardMetricFuncs() map[string]ottl.Factory[ottlmetric.TransformContext] {
m := standardFuncs[ottlmetric.TransformContext]()
m := ottlfuncs.StandardConverters[ottlmetric.TransformContext]()
hasAttributeOnDatapointFactory := newHasAttributeOnDatapointFactory()
hasAttributeKeyOnDatapointFactory := newHasAttributeKeyOnDatapointFactory()
m[hasAttributeOnDatapointFactory.Name()] = hasAttributeOnDatapointFactory
Expand All @@ -37,36 +37,15 @@ func StandardMetricFuncs() map[string]ottl.Factory[ottlmetric.TransformContext]
}

func StandardDataPointFuncs() map[string]ottl.Factory[ottldatapoint.TransformContext] {
return standardFuncs[ottldatapoint.TransformContext]()
return ottlfuncs.StandardConverters[ottldatapoint.TransformContext]()
}

func StandardLogFuncs() map[string]ottl.Factory[ottllog.TransformContext] {
return standardFuncs[ottllog.TransformContext]()
return ottlfuncs.StandardConverters[ottllog.TransformContext]()
}

func StandardResourceFuncs() map[string]ottl.Factory[ottlresource.TransformContext] {
return standardFuncs[ottlresource.TransformContext]()
}

func standardFuncs[K any]() map[string]ottl.Factory[K] {
m := ottlfuncs.StandardConverters[K]()
f := newMatchFactory[K]()
m[f.Name()] = f
return m
}

func newMatchFactory[K any]() ottl.Factory[K] {
return ottl.NewFactory("match", nil, createMatchFunction[K])
}

func createMatchFunction[K any](_ ottl.FunctionContext, _ ottl.Arguments) (ottl.ExprFunc[K], error) {
return matchFn[K]()
}

func matchFn[K any]() (ottl.ExprFunc[K], error) {
return func(context.Context, K) (any, error) {
return true, nil
}, nil
return ottlfuncs.StandardConverters[ottlresource.TransformContext]()
}

type hasAttributeOnDatapointArguments struct {
Expand Down
16 changes: 16 additions & 0 deletions pkg/ottl/contexts/ottldatapoint/datapoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,22 @@ func NewStatements(statements []*ottl.Statement[TransformContext], telemetrySett
return s
}

type ConditionSequenceOption func(*ottl.ConditionSequence[TransformContext])

func WithConditionSequenceErrorMode(errorMode ottl.ErrorMode) ConditionSequenceOption {
return func(c *ottl.ConditionSequence[TransformContext]) {
ottl.WithConditionSequenceErrorMode[TransformContext](errorMode)(c)
}
}

func NewConditionSequence(conditions []*ottl.Condition[TransformContext], telemetrySettings component.TelemetrySettings, options ...ConditionSequenceOption) ottl.ConditionSequence[TransformContext] {
c := ottl.NewConditionSequence(conditions, telemetrySettings)
for _, op := range options {
op(&c)
}
return c
}

var symbolTable = map[ottl.EnumSymbol]ottl.Enum{
"FLAG_NONE": 0,
"FLAG_NO_RECORDED_VALUE": 1,
Expand Down
16 changes: 16 additions & 0 deletions pkg/ottl/contexts/ottllog/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,22 @@ func NewStatements(statements []*ottl.Statement[TransformContext], telemetrySett
return s
}

type ConditionSequenceOption func(*ottl.ConditionSequence[TransformContext])

func WithConditionSequenceErrorMode(errorMode ottl.ErrorMode) ConditionSequenceOption {
return func(c *ottl.ConditionSequence[TransformContext]) {
ottl.WithConditionSequenceErrorMode[TransformContext](errorMode)(c)
}
}

func NewConditionSequence(conditions []*ottl.Condition[TransformContext], telemetrySettings component.TelemetrySettings, options ...ConditionSequenceOption) ottl.ConditionSequence[TransformContext] {
c := ottl.NewConditionSequence(conditions, telemetrySettings)
for _, op := range options {
op(&c)
}
return c
}

var symbolTable = map[ottl.EnumSymbol]ottl.Enum{
"SEVERITY_NUMBER_UNSPECIFIED": ottl.Enum(plog.SeverityNumberUnspecified),
"SEVERITY_NUMBER_TRACE": ottl.Enum(plog.SeverityNumberTrace),
Expand Down
16 changes: 16 additions & 0 deletions pkg/ottl/contexts/ottlmetric/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,22 @@ func NewStatements(statements []*ottl.Statement[TransformContext], telemetrySett
return s
}

type ConditionSequenceOption func(*ottl.ConditionSequence[TransformContext])

func WithConditionSequenceErrorMode(errorMode ottl.ErrorMode) ConditionSequenceOption {
return func(c *ottl.ConditionSequence[TransformContext]) {
ottl.WithConditionSequenceErrorMode[TransformContext](errorMode)(c)
}
}

func NewConditionSequence(conditions []*ottl.Condition[TransformContext], telemetrySettings component.TelemetrySettings, options ...ConditionSequenceOption) ottl.ConditionSequence[TransformContext] {
c := ottl.NewConditionSequence(conditions, telemetrySettings)
for _, op := range options {
op(&c)
}
return c
}

var symbolTable = internal.MetricSymbolTable

func parseEnum(val *ottl.EnumSymbol) (*ottl.Enum, error) {
Expand Down
16 changes: 16 additions & 0 deletions pkg/ottl/contexts/ottlresource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ func NewStatements(statements []*ottl.Statement[TransformContext], telemetrySett
return s
}

type ConditionSequenceOption func(*ottl.ConditionSequence[TransformContext])

func WithConditionSequenceErrorMode(errorMode ottl.ErrorMode) ConditionSequenceOption {
return func(c *ottl.ConditionSequence[TransformContext]) {
ottl.WithConditionSequenceErrorMode[TransformContext](errorMode)(c)
}
}

func NewConditionSequence(conditions []*ottl.Condition[TransformContext], telemetrySettings component.TelemetrySettings, options ...ConditionSequenceOption) ottl.ConditionSequence[TransformContext] {
c := ottl.NewConditionSequence(conditions, telemetrySettings)
for _, op := range options {
op(&c)
}
return c
}

func parseEnum(_ *ottl.EnumSymbol) (*ottl.Enum, error) {
return nil, fmt.Errorf("resource context does not provide Enum support")
}
Expand Down
16 changes: 16 additions & 0 deletions pkg/ottl/contexts/ottlscope/scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,22 @@ func NewStatements(statements []*ottl.Statement[TransformContext], telemetrySett
return s
}

type ConditionSequenceOption func(*ottl.ConditionSequence[TransformContext])

func WithConditionSequenceErrorMode(errorMode ottl.ErrorMode) ConditionSequenceOption {
return func(c *ottl.ConditionSequence[TransformContext]) {
ottl.WithConditionSequenceErrorMode[TransformContext](errorMode)(c)
}
}

func NewConditionSequence(conditions []*ottl.Condition[TransformContext], telemetrySettings component.TelemetrySettings, options ...ConditionSequenceOption) ottl.ConditionSequence[TransformContext] {
c := ottl.NewConditionSequence(conditions, telemetrySettings)
for _, op := range options {
op(&c)
}
return c
}

func parseEnum(_ *ottl.EnumSymbol) (*ottl.Enum, error) {
return nil, fmt.Errorf("instrumentation scope context does not provide Enum support")
}
Expand Down
Loading

0 comments on commit 95a77c1

Please sign in to comment.