Skip to content

Commit

Permalink
[pkg/ottl] Define functions using a Factory interface (#18822)
Browse files Browse the repository at this point in the history
Convert OTTL factories to be defined with an interface

Co-authored-by: Evan Bradley <evan-bradley@users.noreply.github.com>
  • Loading branch information
evan-bradley and evan-bradley authored May 3, 2023
1 parent 546c0da commit b2290e6
Show file tree
Hide file tree
Showing 69 changed files with 1,320 additions and 424 deletions.
18 changes: 18 additions & 0 deletions .chloggen/ottl-function-factory-interface.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: breaking

# 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: Reimplement all OTTL function factories to implement the `ottl.Factory` interface.

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

# (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: |
The `ottl.Factory` interface allows making factories extendable and defines
canonical names for the functions across components using the OTTL.
79 changes: 46 additions & 33 deletions internal/filter/filterottl/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ import (
// NewBoolExprForSpan creates a BoolExpr[ottlspan.TransformContext] that will return true if any of the given OTTL conditions evaluate to true.
// The passed in functions should use the ottlspan.TransformContext.
// If a function named `drop` 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]interface{}, errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottlspan.TransformContext], error) {
if _, ok := functions["drop"]; !ok {
functions["drop"] = drop[ottlspan.TransformContext]
func NewBoolExprForSpan(conditions []string, functions map[string]ottl.Factory[ottlspan.TransformContext], errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottlspan.TransformContext], error) {
drop := newDropFactory[ottlspan.TransformContext]()
if _, ok := functions[drop.Name()]; !ok {
functions[drop.Name()] = drop
}
statmentsStr := conditionsToStatements(conditions)
parser, err := ottlspan.NewParser(functions, set)
Expand All @@ -52,9 +53,10 @@ func NewBoolExprForSpan(conditions []string, functions map[string]interface{}, e
// 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 `drop` 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]interface{}, errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottlspanevent.TransformContext], error) {
if _, ok := functions["drop"]; !ok {
functions["drop"] = drop[ottlspanevent.TransformContext]
func NewBoolExprForSpanEvent(conditions []string, functions map[string]ottl.Factory[ottlspanevent.TransformContext], errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottlspanevent.TransformContext], error) {
drop := newDropFactory[ottlspanevent.TransformContext]()
if _, ok := functions[drop.Name()]; !ok {
functions[drop.Name()] = drop
}
statmentsStr := conditionsToStatements(conditions)
parser, err := ottlspanevent.NewParser(functions, set)
Expand All @@ -72,9 +74,10 @@ func NewBoolExprForSpanEvent(conditions []string, functions map[string]interface
// 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 `drop` 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]interface{}, errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottlmetric.TransformContext], error) {
if _, ok := functions["drop"]; !ok {
functions["drop"] = drop[ottlmetric.TransformContext]
func NewBoolExprForMetric(conditions []string, functions map[string]ottl.Factory[ottlmetric.TransformContext], errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottlmetric.TransformContext], error) {
drop := newDropFactory[ottlmetric.TransformContext]()
if _, ok := functions[drop.Name()]; !ok {
functions[drop.Name()] = drop
}
statmentsStr := conditionsToStatements(conditions)
parser, err := ottlmetric.NewParser(functions, set)
Expand All @@ -92,9 +95,10 @@ func NewBoolExprForMetric(conditions []string, functions map[string]interface{},
// 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 `drop` 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]interface{}, errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottldatapoint.TransformContext], error) {
if _, ok := functions["drop"]; !ok {
functions["drop"] = drop[ottldatapoint.TransformContext]
func NewBoolExprForDataPoint(conditions []string, functions map[string]ottl.Factory[ottldatapoint.TransformContext], errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottldatapoint.TransformContext], error) {
drop := newDropFactory[ottldatapoint.TransformContext]()
if _, ok := functions[drop.Name()]; !ok {
functions[drop.Name()] = drop
}
statmentsStr := conditionsToStatements(conditions)
parser, err := ottldatapoint.NewParser(functions, set)
Expand All @@ -112,9 +116,10 @@ func NewBoolExprForDataPoint(conditions []string, functions map[string]interface
// 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 `drop` 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]interface{}, errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottllog.TransformContext], error) {
if _, ok := functions["drop"]; !ok {
functions["drop"] = drop[ottllog.TransformContext]
func NewBoolExprForLog(conditions []string, functions map[string]ottl.Factory[ottllog.TransformContext], errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottllog.TransformContext], error) {
drop := newDropFactory[ottllog.TransformContext]()
if _, ok := functions[drop.Name()]; !ok {
functions[drop.Name()] = drop
}
statmentsStr := conditionsToStatements(conditions)
parser, err := ottllog.NewParser(functions, set)
Expand All @@ -137,41 +142,49 @@ func conditionsToStatements(conditions []string) []string {
return statements
}

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

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

func StandardMetricFuncs() map[string]interface{} {
func StandardMetricFuncs() map[string]ottl.Factory[ottlmetric.TransformContext] {
return standardFuncs[ottlmetric.TransformContext]()
}

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

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

func standardFuncs[K any]() map[string]interface{} {
return map[string]interface{}{
"TraceID": ottlfuncs.TraceID[K],
"SpanID": ottlfuncs.SpanID[K],
"IsMatch": ottlfuncs.IsMatch[K],
"Concat": ottlfuncs.Concat[K],
"Split": ottlfuncs.Split[K],
"Int": ottlfuncs.Int[K],
"ConvertCase": ottlfuncs.ConvertCase[K],
"Substring": ottlfuncs.Substring[K],
"drop": drop[K],
}
func standardFuncs[K any]() map[string]ottl.Factory[K] {
return ottl.CreateFactoryMap(
ottlfuncs.NewTraceIDFactory[K](),
ottlfuncs.NewSpanIDFactory[K](),
ottlfuncs.NewIsMatchFactory[K](),
ottlfuncs.NewConcatFactory[K](),
ottlfuncs.NewSplitFactory[K](),
ottlfuncs.NewIntFactory[K](),
ottlfuncs.NewConvertCaseFactory[K](),
ottlfuncs.NewSubstringFactory[K](),
newDropFactory[K](),
)
}

func newDropFactory[K any]() ottl.Factory[K] {
return ottl.NewFactory("drop", nil, createDropFunction[K])
}

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

func drop[K any]() (ottl.ExprFunc[K], error) {
func dropFn[K any]() (ottl.ExprFunc[K], error) {
return func(context.Context, K) (interface{}, error) {
return true, nil
}, nil
Expand Down
22 changes: 11 additions & 11 deletions pkg/ottl/boolean_value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func comparisonHelper(left any, right any, op string) *comparison {
}

func Test_newComparisonEvaluator(t *testing.T) {
p, _ := NewParser[any](
p, _ := NewParser(
defaultFunctionsForTests(),
testParsePath,
componenttest.NewNopTelemetrySettings(),
Expand Down Expand Up @@ -131,7 +131,7 @@ func Test_newComparisonEvaluator(t *testing.T) {
}

func Test_newConditionEvaluator_invalid(t *testing.T) {
p, _ := NewParser[any](
p, _ := NewParser(
defaultFunctionsForTests(),
testParsePath,
componenttest.NewNopTelemetrySettings(),
Expand Down Expand Up @@ -163,23 +163,23 @@ func Test_newConditionEvaluator_invalid(t *testing.T) {
}
}

func True[K any]() (ExprFunc[K], error) {
return func(ctx context.Context, tCtx K) (interface{}, error) {
func True() (ExprFunc[any], error) {
return func(ctx context.Context, tCtx any) (interface{}, error) {
return true, nil
}, nil
}
func False[K any]() (ExprFunc[K], error) {
return func(ctx context.Context, tCtx K) (interface{}, error) {
func False() (ExprFunc[any], error) {
return func(ctx context.Context, tCtx any) (interface{}, error) {
return false, nil
}, nil
}

func Test_newBooleanExpressionEvaluator(t *testing.T) {
functions := defaultFunctionsForTests()
functions["True"] = True[any]
functions["False"] = False[any]
functions["True"] = createFactory("True", &struct{}{}, True)
functions["False"] = createFactory("False", &struct{}{}, False)

p, _ := NewParser[any](
p, _ := NewParser(
functions,
testParsePath,
componenttest.NewNopTelemetrySettings(),
Expand Down Expand Up @@ -560,9 +560,9 @@ func Test_newBooleanExpressionEvaluator(t *testing.T) {
}

func Test_newBooleanExpressionEvaluator_invalid(t *testing.T) {
functions := map[string]interface{}{"Hello": hello[interface{}]}
functions := map[string]Factory[any]{"Hello": createFactory("Hello", &struct{}{}, hello)}

p, _ := NewParser[any](
p, _ := NewParser(
functions,
testParsePath,
componenttest.NewNopTelemetrySettings(),
Expand Down
2 changes: 1 addition & 1 deletion pkg/ottl/contexts/ottldatapoint/datapoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func (tCtx TransformContext) getCache() pcommon.Map {
return tCtx.cache
}

func NewParser(functions map[string]interface{}, telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) {
func NewParser(functions map[string]ottl.Factory[TransformContext], telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) {
p, err := ottl.NewParser[TransformContext](
functions,
parsePath,
Expand Down
2 changes: 1 addition & 1 deletion pkg/ottl/contexts/ottllog/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (tCtx TransformContext) getCache() pcommon.Map {
return tCtx.cache
}

func NewParser(functions map[string]interface{}, telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) {
func NewParser(functions map[string]ottl.Factory[TransformContext], telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) {
p, err := ottl.NewParser[TransformContext](
functions,
parsePath,
Expand Down
2 changes: 1 addition & 1 deletion pkg/ottl/contexts/ottlmetric/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (tCtx TransformContext) getCache() pcommon.Map {
return tCtx.cache
}

func NewParser(functions map[string]interface{}, telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) {
func NewParser(functions map[string]ottl.Factory[TransformContext], telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) {
p, err := ottl.NewParser[TransformContext](
functions,
parsePath,
Expand Down
2 changes: 1 addition & 1 deletion pkg/ottl/contexts/ottlresource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (tCtx TransformContext) getCache() pcommon.Map {
return tCtx.cache
}

func NewParser(functions map[string]interface{}, telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) {
func NewParser(functions map[string]ottl.Factory[TransformContext], telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) {
p, err := ottl.NewParser[TransformContext](
functions,
parsePath,
Expand Down
2 changes: 1 addition & 1 deletion pkg/ottl/contexts/ottlscope/scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (tCtx TransformContext) getCache() pcommon.Map {
return tCtx.cache
}

func NewParser(functions map[string]interface{}, telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) {
func NewParser(functions map[string]ottl.Factory[TransformContext], telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) {
p, err := ottl.NewParser[TransformContext](
functions,
parsePath,
Expand Down
2 changes: 1 addition & 1 deletion pkg/ottl/contexts/ottlspan/span.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (tCtx TransformContext) getCache() pcommon.Map {
return tCtx.cache
}

func NewParser(functions map[string]interface{}, telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) {
func NewParser(functions map[string]ottl.Factory[TransformContext], telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) {
p, err := ottl.NewParser[TransformContext](
functions,
parsePath,
Expand Down
2 changes: 1 addition & 1 deletion pkg/ottl/contexts/ottlspanevent/span_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (tCtx TransformContext) getCache() pcommon.Map {
return tCtx.cache
}

func NewParser(functions map[string]interface{}, telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) {
func NewParser(functions map[string]ottl.Factory[TransformContext], telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) {
p, err := ottl.NewParser[TransformContext](
functions,
parsePath,
Expand Down
6 changes: 3 additions & 3 deletions pkg/ottl/expression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import (
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottltest"
)

func hello[K any]() (ExprFunc[K], error) {
return func(ctx context.Context, tCtx K) (interface{}, error) {
func hello() (ExprFunc[any], error) {
return func(ctx context.Context, tCtx any) (any, error) {
return "world", nil
}, nil
}
Expand Down Expand Up @@ -289,7 +289,7 @@ func Test_newGetter(t *testing.T) {
},
}

functions := map[string]interface{}{"Hello": hello[interface{}]}
functions := CreateFactoryMap(createFactory("Hello", &struct{}{}, hello))

p, _ := NewParser[any](
functions,
Expand Down
96 changes: 96 additions & 0 deletions pkg/ottl/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ottl // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"

import "go.opentelemetry.io/collector/component"

// Arguments holds the arguments for an OTTL function, with arguments
// specified as fields on a struct. Argument ordering is defined
type Arguments interface{}

// FunctionContext contains data provided by the Collector
// component to the OTTL for use in functions.
type FunctionContext struct {
Set component.TelemetrySettings
}

// Factory defines an OTTL function factory that will generate an OTTL
// function to be called based on an invocation within a statement.
type Factory[K any] interface {
// Name is the canonical name to be used by the user when invocating
// the function generated by this Factory.
Name() string

// CreateDefaultArguments initializes an Arguments struct specific to this
// Factory containing the arguments for the function.
CreateDefaultArguments() Arguments

// CreateFunction creates an OTTL function that will use the given Arguments.
CreateFunction(fCtx FunctionContext, args Arguments) (ExprFunc[K], error)

// Disallow implementations outside this package.
unexportedFactoryFunc()
}

type CreateFunctionFunc[K any] func(fCtx FunctionContext, args Arguments) (ExprFunc[K], error)

type factory[K any] struct {
name string
args Arguments
createFunctionFunc CreateFunctionFunc[K]
}

// nolint:unused
func (f *factory[K]) unexportedFactoryFunc() {}

func (f *factory[K]) Name() string {
return f.name
}

func (f *factory[K]) CreateDefaultArguments() Arguments {
return f.args
}

func (f *factory[K]) CreateFunction(fCtx FunctionContext, args Arguments) (ExprFunc[K], error) {
return f.createFunctionFunc(fCtx, args)
}

type FactoryOption[K any] func(factory *factory[K])

func NewFactory[K any](name string, args Arguments, createFunctionFunc CreateFunctionFunc[K], options ...FactoryOption[K]) Factory[K] {
f := &factory[K]{
name: name,
args: args,
createFunctionFunc: createFunctionFunc,
}

for _, option := range options {
option(f)
}

return f
}

// CreateFactoryMap takes a list of factories and returns a map of Factories
// keyed on their canonical names.
func CreateFactoryMap[K any](factories ...Factory[K]) map[string]Factory[K] {
factoryMap := map[string]Factory[K]{}

for _, fn := range factories {
factoryMap[fn.Name()] = fn
}

return factoryMap
}
Loading

0 comments on commit b2290e6

Please sign in to comment.