diff --git a/internal/filter/filtermetric/filtermetric.go b/internal/filter/filtermetric/filtermetric.go index 63baffbb1bd1..011ad369fbe1 100644 --- a/internal/filter/filtermetric/filtermetric.go +++ b/internal/filter/filtermetric/filtermetric.go @@ -4,15 +4,28 @@ package filtermetric // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filtermetric" import ( + "go.opentelemetry.io/collector/featuregate" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/expr" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterconfig" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterottl" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric" ) +var UseOTTLBridge = featuregate.GlobalRegistry().MustRegister( + "filter.filtermetric.useOTTLBridge", + featuregate.StageAlpha, + featuregate.WithRegisterDescription("When enabled, filtermetric will convert filtermetric configuration to OTTL and use filterottl evaluation"), + featuregate.WithRegisterReferenceURL("https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/18642"), +) + // NewSkipExpr creates a BoolExpr that on evaluation returns true if a metric should NOT be processed or kept. // The logic determining if a metric should be processed is based on include and exclude settings. // Include properties are checked before exclude settings are checked. func NewSkipExpr(include *filterconfig.MetricMatchProperties, exclude *filterconfig.MetricMatchProperties) (expr.BoolExpr[ottlmetric.TransformContext], error) { + if UseOTTLBridge.IsEnabled() { + return filterottl.NewMetricSkipExprBridge(include, exclude) + } var matchers []expr.BoolExpr[ottlmetric.TransformContext] inclExpr, err := newExpr(include) if err != nil { diff --git a/internal/filter/filtermetric/filtermetric_test.go b/internal/filter/filtermetric/filtermetric_test.go index ad850d36e556..1ba06482d1b7 100644 --- a/internal/filter/filtermetric/filtermetric_test.go +++ b/internal/filter/filtermetric/filtermetric_test.go @@ -5,13 +5,16 @@ package filtermetric import ( "context" + "fmt" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterconfig" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterottl" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterset" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric" ) @@ -86,3 +89,123 @@ func TestMatcherMatches(t *testing.T) { }) } } + +func Test_NewSkipExpr_With_Bridge(t *testing.T) { + tests := []struct { + name string + include *filterconfig.MetricMatchProperties + exclude *filterconfig.MetricMatchProperties + err error + }{ + // Metric Name + { + name: "single static metric name include", + include: &filterconfig.MetricMatchProperties{ + MatchType: filterconfig.MetricStrict, + MetricNames: []string{"metricA"}, + }, + }, + { + name: "multiple static service name include", + include: &filterconfig.MetricMatchProperties{ + MatchType: filterconfig.MetricStrict, + MetricNames: []string{"metricB", "metricC"}, + }, + }, + { + name: "single regex service name include", + include: &filterconfig.MetricMatchProperties{ + MatchType: filterconfig.MetricRegexp, + MetricNames: []string{".*A"}, + }, + }, + { + name: "multiple regex service name include", + include: &filterconfig.MetricMatchProperties{ + MatchType: filterconfig.MetricRegexp, + MetricNames: []string{".*B", ".*C"}, + }, + }, + { + name: "single static metric name exclude", + exclude: &filterconfig.MetricMatchProperties{ + MatchType: filterconfig.MetricStrict, + MetricNames: []string{"metricA"}, + }, + }, + { + name: "multiple static service name exclude", + exclude: &filterconfig.MetricMatchProperties{ + MatchType: filterconfig.MetricStrict, + MetricNames: []string{"metricB", "metricC"}, + }, + }, + { + name: "single regex service name exclude", + exclude: &filterconfig.MetricMatchProperties{ + MatchType: filterconfig.MetricRegexp, + MetricNames: []string{".*A"}, + }, + }, + { + name: "multiple regex service name exclude", + exclude: &filterconfig.MetricMatchProperties{ + MatchType: filterconfig.MetricRegexp, + MetricNames: []string{".*B", ".*C"}, + }, + }, + + // Expression + { + name: "expression errors", + include: &filterconfig.MetricMatchProperties{ + MatchType: filterconfig.MetricExpr, + Expressions: []string{"MetricName == metricA"}, + }, + err: fmt.Errorf("expressions configuration cannot be converted to OTTL - see https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/filterprocessor#configuration for OTTL configuration"), + }, + + // Complex + { + name: "complex", + include: &filterconfig.MetricMatchProperties{ + MatchType: filterconfig.MetricStrict, + MetricNames: []string{"metricA"}, + }, + exclude: &filterconfig.MetricMatchProperties{ + MatchType: filterconfig.MetricRegexp, + MetricNames: []string{".*B", ".*C"}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + metric := pmetric.NewMetric() + metric.SetName("metricA") + + resource := pcommon.NewResource() + + scope := pcommon.NewInstrumentationScope() + + tCtx := ottlmetric.NewTransformContext(metric, scope, resource) + + boolExpr, err := NewSkipExpr(tt.include, tt.exclude) + require.NoError(t, err) + expectedResult, err := boolExpr.Eval(context.Background(), tCtx) + assert.NoError(t, err) + + ottlBoolExpr, err := filterottl.NewMetricSkipExprBridge(tt.include, tt.exclude) + + if tt.err != nil { + assert.Equal(t, tt.err, err) + } else { + assert.NoError(t, err) + ottlResult, err := ottlBoolExpr.Eval(context.Background(), tCtx) + assert.NoError(t, err) + + assert.Equal(t, expectedResult, ottlResult) + } + }) + } +} diff --git a/internal/filter/filterottl/bridge.go b/internal/filter/filterottl/bridge.go index 398fc09aa759..99147e96a0b3 100644 --- a/internal/filter/filterottl/bridge.go +++ b/internal/filter/filterottl/bridge.go @@ -15,12 +15,14 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterset" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottllog" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlresource" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan" ) const ( serviceNameStaticStatement = `resource.attributes["service.name"] == "%v"` - spanNameStaticStatement = `name == "%v"` + nameStaticStatement = `name == "%v"` spanKindStaticStatement = `kind.deprecated_string == "%v"` scopeNameStaticStatement = `instrumentation_scope.name == "%v"` scopeVersionStaticStatement = `instrumentation_scope.version == "%v"` @@ -30,7 +32,7 @@ const ( severityTextStaticStatement = `severity_text == "%v"` serviceNameRegexStatement = `IsMatch(resource.attributes["service.name"], "%v")` - spanNameRegexStatement = `IsMatch(name, "%v")` + nameRegexStatement = `IsMatch(name, "%v")` spanKindRegexStatement = `IsMatch(kind.deprecated_string, "%v")` scopeNameRegexStatement = `IsMatch(instrumentation_scope.name, "%v")` scopeVersionRegexStatement = `IsMatch(instrumentation_scope.version, "%v")` @@ -83,6 +85,35 @@ func NewLogSkipExprBridge(mc *filterconfig.MatchConfig) (expr.BoolExpr[ottllog.T return NewBoolExprForLog(statements, StandardLogFuncs(), ottl.PropagateError, component.TelemetrySettings{Logger: zap.NewNop()}) } +func NewResourceSkipExprBridge(mc *filterconfig.MatchConfig) (expr.BoolExpr[ottlresource.TransformContext], error) { + statements := make([]string, 0, 2) + if mc.Include != nil { + // OTTL treats resource attributes as attributes for the resource context. + mc.Include.Attributes = mc.Include.Resources + mc.Include.Resources = nil + + statement, err := createStatement(*mc.Include) + if err != nil { + return nil, err + } + statements = append(statements, fmt.Sprintf("not (%v)", statement)) + } + + if mc.Exclude != nil { + // OTTL treats resource attributes as attributes for the resource context. + mc.Exclude.Attributes = mc.Exclude.Resources + mc.Exclude.Resources = nil + + statement, err := createStatement(*mc.Exclude) + if err != nil { + return nil, err + } + statements = append(statements, fmt.Sprintf("%v", statement)) + } + + return NewBoolExprForResource(statements, StandardResourceFuncs(), ottl.PropagateError, component.TelemetrySettings{Logger: zap.NewNop()}) +} + func NewSpanSkipExprBridge(mc *filterconfig.MatchConfig) (expr.BoolExpr[ottlspan.TransformContext], error) { statements := make([]string, 0, 2) if mc.Include != nil { @@ -239,7 +270,7 @@ func createStatementTemplates(matchType filterset.MatchType) (statementTemplates case filterset.Strict: return statementTemplates{ serviceNameStatement: serviceNameStaticStatement, - spanNameStatement: spanNameStaticStatement, + spanNameStatement: nameStaticStatement, spanKindStatement: spanKindStaticStatement, scopeNameStatement: scopeNameStaticStatement, scopeVersionStatement: scopeVersionStaticStatement, @@ -251,7 +282,7 @@ func createStatementTemplates(matchType filterset.MatchType) (statementTemplates case filterset.Regexp: return statementTemplates{ serviceNameStatement: serviceNameRegexStatement, - spanNameStatement: spanNameRegexStatement, + spanNameStatement: nameRegexStatement, spanKindStatement: spanKindRegexStatement, scopeNameStatement: scopeNameRegexStatement, scopeVersionStatement: scopeVersionRegexStatement, @@ -315,3 +346,59 @@ func createSeverityNumberConditions(severityNumberProperties *filterconfig.LogSe severityNumberCondition := fmt.Sprintf(severityNumberStatement, severityNumberProperties.MatchUndefined, severityNumberProperties.Min) return &severityNumberCondition } + +func NewMetricSkipExprBridge(include *filterconfig.MetricMatchProperties, exclude *filterconfig.MetricMatchProperties) (expr.BoolExpr[ottlmetric.TransformContext], error) { + statements := make([]string, 0, 2) + if include != nil { + statement, err := createMetricStatement(*include) + if err != nil { + return nil, err + } + if statement != nil { + statements = append(statements, fmt.Sprintf("not (%v)", *statement)) + } + } + + if exclude != nil { + statement, err := createMetricStatement(*exclude) + if err != nil { + return nil, err + } + if statement != nil { + statements = append(statements, fmt.Sprintf("%v", *statement)) + } + } + + if len(statements) == 0 { + return nil, nil + } + + return NewBoolExprForMetric(statements, StandardMetricFuncs(), ottl.PropagateError, component.TelemetrySettings{Logger: zap.NewNop()}) +} + +func createMetricStatement(mp filterconfig.MetricMatchProperties) (*string, error) { + if mp.MatchType == filterconfig.MetricExpr { + if len(mp.Expressions) == 0 { + return nil, nil + } + return nil, fmt.Errorf("expressions configuration cannot be converted to OTTL - see https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/filterprocessor#configuration for OTTL configuration") + } + + if len(mp.MetricNames) == 0 { + return nil, nil + } + + metricNameStatement := nameStaticStatement + if mp.MatchType == filterconfig.MetricRegexp { + metricNameStatement = nameRegexStatement + } + metricNameConditions := createBasicConditions(metricNameStatement, mp.MetricNames) + var format string + if len(metricNameConditions) > 1 { + format = "(%v)" + } else { + format = "%v" + } + statement := fmt.Sprintf(format, strings.Join(metricNameConditions, " or ")) + return &statement, nil +} diff --git a/internal/filter/filterottl/filter.go b/internal/filter/filterottl/filter.go index 0060f31191e4..19a1f87469b2 100644 --- a/internal/filter/filterottl/filter.go +++ b/internal/filter/filterottl/filter.go @@ -4,8 +4,6 @@ package filterottl // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterottl" import ( - "context" - "go.opentelemetry.io/collector/component" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/expr" @@ -13,9 +11,9 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottldatapoint" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottllog" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlresource" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspanevent" - "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs" ) // NewBoolExprForSpan creates a BoolExpr[ottlspan.TransformContext] that will return true if any of the given OTTL conditions evaluate to true. @@ -123,6 +121,27 @@ func NewBoolExprForLog(conditions []string, functions map[string]ottl.Factory[ot return &s, 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 `drop` 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) { + drop := newDropFactory[ottlresource.TransformContext]() + if _, ok := functions[drop.Name()]; !ok { + functions[drop.Name()] = drop + } + statmentsStr := conditionsToStatements(conditions) + parser, err := ottlresource.NewParser(functions, set) + if err != nil { + return nil, err + } + statements, err := parser.ParseStatements(statmentsStr) + 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 { @@ -130,44 +149,3 @@ func conditionsToStatements(conditions []string) []string { } return statements } - -func StandardSpanFuncs() map[string]ottl.Factory[ottlspan.TransformContext] { - return standardFuncs[ottlspan.TransformContext]() -} - -func StandardSpanEventFuncs() map[string]ottl.Factory[ottlspanevent.TransformContext] { - return standardFuncs[ottlspanevent.TransformContext]() -} - -func StandardMetricFuncs() map[string]ottl.Factory[ottlmetric.TransformContext] { - return standardFuncs[ottlmetric.TransformContext]() -} - -func StandardDataPointFuncs() map[string]ottl.Factory[ottldatapoint.TransformContext] { - return standardFuncs[ottldatapoint.TransformContext]() -} - -func StandardLogFuncs() map[string]ottl.Factory[ottllog.TransformContext] { - return standardFuncs[ottllog.TransformContext]() -} - -func standardFuncs[K any]() map[string]ottl.Factory[K] { - m := ottlfuncs.StandardConverters[K]() - f := newDropFactory[K]() - m[f.Name()] = f - return m -} - -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 dropFn[K any]() (ottl.ExprFunc[K], error) { - return func(context.Context, K) (interface{}, error) { - return true, nil - }, nil -} diff --git a/internal/filter/filterottl/filter_test.go b/internal/filter/filterottl/filter_test.go index ee52b4545a53..8e6a65ebc4c8 100644 --- a/internal/filter/filterottl/filter_test.go +++ b/internal/filter/filterottl/filter_test.go @@ -14,6 +14,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottldatapoint" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottllog" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlresource" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspanevent" ) @@ -227,3 +228,45 @@ func Test_NewBoolExprForLog(t *testing.T) { }) } } + +func Test_NewBoolExprForResource(t *testing.T) { + tests := []struct { + name string + conditions []string + expectedResult bool + }{ + { + name: "basic", + conditions: []string{ + "true == true", + }, + expectedResult: true, + }, + { + name: "multiple", + conditions: []string{ + "false == true", + "true == true", + }, + expectedResult: true, + }, + { + name: "With Converter", + conditions: []string{ + `IsMatch("test", "pass")`, + }, + expectedResult: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resBoolExpr, err := NewBoolExprForResource(tt.conditions, StandardResourceFuncs(), ottl.PropagateError, componenttest.NewNopTelemetrySettings()) + assert.NoError(t, err) + assert.NotNil(t, resBoolExpr) + result, err := resBoolExpr.Eval(context.Background(), ottlresource.TransformContext{}) + assert.NoError(t, err) + assert.Equal(t, tt.expectedResult, result) + }) + } +} diff --git a/processor/filterprocessor/internal/common/functions.go b/internal/filter/filterottl/functions.go similarity index 67% rename from processor/filterprocessor/internal/common/functions.go rename to internal/filter/filterottl/functions.go index a1fb779dfdd9..be36b3999861 100644 --- a/processor/filterprocessor/internal/common/functions.go +++ b/internal/filter/filterottl/functions.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package common // import "github.com/open-telemetry/opentelemetry-collector-contrib/processor/filterprocessor/internal/common" +package filterottl // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterottl" import ( "context" @@ -9,19 +9,64 @@ import ( "go.opentelemetry.io/collector/pdata/pmetric" - "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterottl" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottldatapoint" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottllog" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlresource" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspanevent" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs" ) -func MetricFunctions() map[string]ottl.Factory[ottlmetric.TransformContext] { - funcs := filterottl.StandardMetricFuncs() - hasAttributeKeyOnDatapointFactory := newHasAttributeKeyOnDatapointFactory() - funcs[hasAttributeKeyOnDatapointFactory.Name()] = hasAttributeKeyOnDatapointFactory +func StandardSpanFuncs() map[string]ottl.Factory[ottlspan.TransformContext] { + return standardFuncs[ottlspan.TransformContext]() +} +func StandardSpanEventFuncs() map[string]ottl.Factory[ottlspanevent.TransformContext] { + return standardFuncs[ottlspanevent.TransformContext]() +} + +func StandardMetricFuncs() map[string]ottl.Factory[ottlmetric.TransformContext] { + m := standardFuncs[ottlmetric.TransformContext]() hasAttributeOnDatapointFactory := newHasAttributeOnDatapointFactory() - funcs[hasAttributeOnDatapointFactory.Name()] = hasAttributeOnDatapointFactory - return funcs + hasAttributeKeyOnDatapointFactory := newHasAttributeKeyOnDatapointFactory() + m[hasAttributeOnDatapointFactory.Name()] = hasAttributeOnDatapointFactory + m[hasAttributeKeyOnDatapointFactory.Name()] = hasAttributeKeyOnDatapointFactory + return m +} + +func StandardDataPointFuncs() map[string]ottl.Factory[ottldatapoint.TransformContext] { + return standardFuncs[ottldatapoint.TransformContext]() +} + +func StandardLogFuncs() map[string]ottl.Factory[ottllog.TransformContext] { + return standardFuncs[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 := newDropFactory[K]() + m[f.Name()] = f + return m +} + +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 dropFn[K any]() (ottl.ExprFunc[K], error) { + return func(context.Context, K) (interface{}, error) { + return true, nil + }, nil } type hasAttributeOnDatapointArguments struct { diff --git a/processor/filterprocessor/internal/common/functions_test.go b/internal/filter/filterottl/functions_test.go similarity index 98% rename from processor/filterprocessor/internal/common/functions_test.go rename to internal/filter/filterottl/functions_test.go index 6f6a05d38c6b..a219862daef4 100644 --- a/processor/filterprocessor/internal/common/functions_test.go +++ b/internal/filter/filterottl/functions_test.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package common // import "github.com/open-telemetry/opentelemetry-collector-contrib/processor/filterprocessor/internal/common" +package filterottl import ( "context" diff --git a/processor/filterprocessor/config.go b/processor/filterprocessor/config.go index 0f4234bf2054..a60f672422ca 100644 --- a/processor/filterprocessor/config.go +++ b/processor/filterprocessor/config.go @@ -18,7 +18,6 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterset" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterset/regexp" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" - "github.com/open-telemetry/opentelemetry-collector-contrib/processor/filterprocessor/internal/common" ) // Config defines configuration for Resource processor. @@ -290,7 +289,7 @@ func (cfg *Config) Validate() error { } if cfg.Metrics.MetricConditions != nil { - _, err := filterottl.NewBoolExprForMetric(cfg.Metrics.MetricConditions, common.MetricFunctions(), ottl.PropagateError, component.TelemetrySettings{Logger: zap.NewNop()}) + _, err := filterottl.NewBoolExprForMetric(cfg.Metrics.MetricConditions, filterottl.StandardMetricFuncs(), ottl.PropagateError, component.TelemetrySettings{Logger: zap.NewNop()}) errors = multierr.Append(errors, err) } diff --git a/processor/filterprocessor/metrics.go b/processor/filterprocessor/metrics.go index 2012838506dc..613c95eadda8 100644 --- a/processor/filterprocessor/metrics.go +++ b/processor/filterprocessor/metrics.go @@ -22,7 +22,6 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottldatapoint" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlresource" - "github.com/open-telemetry/opentelemetry-collector-contrib/processor/filterprocessor/internal/common" ) type filterMetricProcessor struct { @@ -39,7 +38,7 @@ func newFilterMetricProcessor(set component.TelemetrySettings, cfg *Config) (*fi } if cfg.Metrics.MetricConditions != nil || cfg.Metrics.DataPointConditions != nil { if cfg.Metrics.MetricConditions != nil { - fsp.skipMetricExpr, err = filterottl.NewBoolExprForMetric(cfg.Metrics.MetricConditions, common.MetricFunctions(), cfg.ErrorMode, set) + fsp.skipMetricExpr, err = filterottl.NewBoolExprForMetric(cfg.Metrics.MetricConditions, filterottl.StandardMetricFuncs(), cfg.ErrorMode, set) if err != nil { return nil, err } @@ -172,6 +171,32 @@ func (fmp *filterMetricProcessor) processMetrics(ctx context.Context, md pmetric } func newSkipResExpr(include *filterconfig.MetricMatchProperties, exclude *filterconfig.MetricMatchProperties) (expr.BoolExpr[ottlresource.TransformContext], error) { + if filtermetric.UseOTTLBridge.IsEnabled() { + mp := filterconfig.MatchConfig{} + + if include != nil { + mp.Include = &filterconfig.MatchProperties{ + Config: filterset.Config{ + MatchType: filterset.MatchType(include.MatchType), + RegexpConfig: include.RegexpConfig, + }, + Resources: include.ResourceAttributes, + } + } + + if exclude != nil { + mp.Exclude = &filterconfig.MatchProperties{ + Config: filterset.Config{ + MatchType: filterset.MatchType(exclude.MatchType), + RegexpConfig: exclude.RegexpConfig, + }, + Resources: exclude.ResourceAttributes, + } + } + + return filterottl.NewResourceSkipExprBridge(&mp) + } + var matchers []expr.BoolExpr[ottlresource.TransformContext] inclExpr, err := newResExpr(include) if err != nil { diff --git a/processor/filterprocessor/metrics_test.go b/processor/filterprocessor/metrics_test.go index c2aa12c82f8b..b3d5ceecc906 100644 --- a/processor/filterprocessor/metrics_test.go +++ b/processor/filterprocessor/metrics_test.go @@ -20,7 +20,10 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/goldendataset" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterconfig" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterottl" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterset" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlresource" ) type metricNameTest struct { @@ -867,3 +870,180 @@ func fillMetricFive(m pmetric.Metric) { dataPoint1.Attributes().PutStr("attr2", "test2") dataPoint1.Attributes().PutStr("attr3", "test3") } + +func Test_ResourceSkipExpr_With_Bridge(t *testing.T) { + tests := []struct { + name string + condition *filterconfig.MatchConfig + }{ + // resource attributes + { + name: "single static resource attribute include", + condition: &filterconfig.MatchConfig{ + Include: &filterconfig.MatchProperties{ + Config: filterset.Config{ + MatchType: filterset.Strict, + }, + Resources: []filterconfig.Attribute{ + { + Key: "service.name", + Value: "svcA", + }, + }, + }, + }, + }, + { + name: "multiple static resource attribute include", + condition: &filterconfig.MatchConfig{ + Include: &filterconfig.MatchProperties{ + Config: filterset.Config{ + MatchType: filterset.Strict, + }, + + Resources: []filterconfig.Attribute{ + { + Key: "service.name", + Value: "svc2", + }, + { + Key: "service.version", + Value: "v1", + }, + }, + }, + }, + }, + { + name: "single regex resource attribute include", + condition: &filterconfig.MatchConfig{ + Include: &filterconfig.MatchProperties{ + Config: filterset.Config{ + MatchType: filterset.Regexp, + }, + Resources: []filterconfig.Attribute{ + { + Key: "service.name", + Value: "svc.*", + }, + }, + }, + }, + }, + { + name: "multiple regex resource attribute include", + condition: &filterconfig.MatchConfig{ + Include: &filterconfig.MatchProperties{ + Config: filterset.Config{ + MatchType: filterset.Regexp, + }, + Resources: []filterconfig.Attribute{ + { + Key: "service.name", + Value: ".*2", + }, + { + Key: "service.name", + Value: ".*3", + }, + }, + }, + }, + }, + { + name: "single static resource attribute exclude", + condition: &filterconfig.MatchConfig{ + Exclude: &filterconfig.MatchProperties{ + Config: filterset.Config{ + MatchType: filterset.Strict, + }, + Resources: []filterconfig.Attribute{ + { + Key: "service.name", + Value: "svcA", + }, + }, + }, + }, + }, + { + name: "multiple static resource attribute exclude", + condition: &filterconfig.MatchConfig{ + Exclude: &filterconfig.MatchProperties{ + Config: filterset.Config{ + MatchType: filterset.Strict, + }, + + Resources: []filterconfig.Attribute{ + { + Key: "service.name", + Value: "svc2", + }, + { + Key: "service.version", + Value: "v1", + }, + }, + }, + }, + }, + { + name: "single regex resource attribute exclude", + condition: &filterconfig.MatchConfig{ + Exclude: &filterconfig.MatchProperties{ + Config: filterset.Config{ + MatchType: filterset.Regexp, + }, + Resources: []filterconfig.Attribute{ + { + Key: "service.name", + Value: "svc.*", + }, + }, + }, + }, + }, + { + name: "multiple regex resource attribute exclude", + condition: &filterconfig.MatchConfig{ + Exclude: &filterconfig.MatchProperties{ + Config: filterset.Config{ + MatchType: filterset.Regexp, + }, + Resources: []filterconfig.Attribute{ + { + Key: "service.name", + Value: ".*2", + }, + { + Key: "service.name", + Value: ".*3", + }, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resource := pcommon.NewResource() + resource.Attributes().PutStr("test", "test") + + tCtx := ottlresource.NewTransformContext(resource) + + boolExpr, err := newSkipResExpr(filterconfig.CreateMetricMatchPropertiesFromDefault(tt.condition.Include), filterconfig.CreateMetricMatchPropertiesFromDefault(tt.condition.Exclude)) + require.NoError(t, err) + expectedResult, err := boolExpr.Eval(context.Background(), tCtx) + assert.NoError(t, err) + + ottlBoolExpr, err := filterottl.NewResourceSkipExprBridge(tt.condition) + + assert.NoError(t, err) + ottlResult, err := ottlBoolExpr.Eval(context.Background(), tCtx) + assert.NoError(t, err) + + assert.Equal(t, expectedResult, ottlResult) + }) + } +}