Skip to content

Commit 3288793

Browse files
odubajDTevan-bradleyedmocosta
authored
[pkg/ottl] Support dynamic pattern key in IsMatch function (#43727)
Co-authored-by: Evan Bradley <[email protected]> Co-authored-by: Edmo Vamerlatti Costa <[email protected]>
1 parent b66e064 commit 3288793

12 files changed

+384
-83
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. receiver/filelog)
7+
component: pkg/ottl
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: "Support taking match patterns from runtime data in the `IsMatch` function."
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [43555]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: []

pkg/ottl/e2e/e2e_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,6 +1409,13 @@ func Test_e2e_ottl_features(t *testing.T) {
14091409
tCtx.GetLogRecord().Attributes().PutStr("test", "pass")
14101410
},
14111411
},
1412+
{
1413+
name: "where clause without comparator dynamic pattern",
1414+
statement: `set(attributes["test"], "pass") where IsMatch(body, Concat(["operation", "[AC]"], ""))`,
1415+
want: func(tCtx ottllog.TransformContext) {
1416+
tCtx.GetLogRecord().Attributes().PutStr("test", "pass")
1417+
},
1418+
},
14121419
{
14131420
name: "where clause with Converter return value",
14141421
statement: `set(attributes["test"], "pass") where body == Concat(["operation", "A"], "")`,

pkg/ottl/functions.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ type Enum int64
2626
// EnumSymbol is how OTTL represents an enum's string value.
2727
type EnumSymbol string
2828

29-
const InvalidRegexErrMsg = "the regex pattern supplied to %s '%q' is not a valid pattern: %w"
30-
3129
func buildOriginalText(path *path) string {
3230
var builder strings.Builder
3331
if path.Context != "" {

pkg/ottl/ottlfuncs/func_delete_matching_keys.go

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ package ottlfuncs // import "github.com/open-telemetry/opentelemetry-collector-c
66
import (
77
"context"
88
"errors"
9-
"fmt"
10-
"regexp"
119

1210
"go.opentelemetry.io/collector/pdata/pcommon"
1311

@@ -19,8 +17,6 @@ type DeleteMatchingKeysArguments[K any] struct {
1917
Pattern ottl.StringGetter[K]
2018
}
2119

22-
const invalidRegexErrMsg = "the regex pattern supplied to delete_matching_keys '%q' is not a valid pattern: %w"
23-
2420
func NewDeleteMatchingKeysFactory[K any]() ottl.Factory[K] {
2521
return ottl.NewFactory("delete_matching_keys", &DeleteMatchingKeysArguments[K]{}, createDeleteMatchingKeysFunction[K])
2622
}
@@ -36,26 +32,14 @@ func createDeleteMatchingKeysFunction[K any](_ ottl.FunctionContext, oArgs ottl.
3632
}
3733

3834
func deleteMatchingKeys[K any](target ottl.PMapGetSetter[K], pattern ottl.StringGetter[K]) (ottl.ExprFunc[K], error) {
39-
literalPattern, ok := ottl.GetLiteralValue(pattern)
40-
var compiledPattern *regexp.Regexp
41-
var err error
42-
if ok {
43-
compiledPattern, err = regexp.Compile(literalPattern)
44-
if err != nil {
45-
return nil, fmt.Errorf(invalidRegexErrMsg, literalPattern, err)
46-
}
35+
compiledPattern, err := newDynamicRegex("delete_matching_keys", pattern)
36+
if err != nil {
37+
return nil, err
4738
}
4839
return func(ctx context.Context, tCtx K) (any, error) {
49-
cp := compiledPattern
50-
if cp == nil {
51-
patternVal, err := pattern.Get(ctx, tCtx)
52-
if err != nil {
53-
return nil, err
54-
}
55-
cp, err = regexp.Compile(patternVal)
56-
if err != nil {
57-
return nil, fmt.Errorf(invalidRegexErrMsg, patternVal, err)
58-
}
40+
cp, err := compiledPattern.compile(ctx, tCtx)
41+
if err != nil {
42+
return nil, err
5943
}
6044
val, err := target.Get(ctx, tCtx)
6145
if err != nil {

pkg/ottl/ottlfuncs/func_extract_grok_patterns.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func extractGrokPatterns[K any](target, pattern ottl.StringGetter[K], nco ottl.O
7171
if ok {
7272
err = g.Compile(literalPattern, namedCapturesOnly)
7373
if err != nil {
74-
return nil, fmt.Errorf(ottl.InvalidRegexErrMsg, "ExtractGrokPatterns", literalPattern, err)
74+
return nil, fmt.Errorf(invalidRegexErrMsg, "ExtractGrokPatterns", literalPattern, err)
7575
}
7676
compiled = true
7777
}
@@ -88,7 +88,7 @@ func extractGrokPatterns[K any](target, pattern ottl.StringGetter[K], nco ottl.O
8888
}
8989
err = g.Compile(patternVal, namedCapturesOnly)
9090
if err != nil {
91-
return nil, fmt.Errorf(ottl.InvalidRegexErrMsg, "ExtractGrokPatterns", patternVal, err)
91+
return nil, fmt.Errorf(invalidRegexErrMsg, "ExtractGrokPatterns", patternVal, err)
9292
}
9393
}
9494

pkg/ottl/ottlfuncs/func_extract_patterns.go

Lines changed: 7 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ package ottlfuncs // import "github.com/open-telemetry/opentelemetry-collector-c
66
import (
77
"context"
88
"errors"
9-
"fmt"
10-
"regexp"
119

1210
"go.opentelemetry.io/collector/pdata/pcommon"
1311

@@ -34,43 +32,17 @@ func createExtractPatternsFunction[K any](_ ottl.FunctionContext, oArgs ottl.Arg
3432
}
3533

3634
func extractPatterns[K any](target, pattern ottl.StringGetter[K]) (ottl.ExprFunc[K], error) {
37-
literalPattern, ok := ottl.GetLiteralValue(pattern)
38-
var compiledPattern *regexp.Regexp
39-
var err error
40-
compiled := false
41-
if ok {
42-
compiledPattern, err = regexp.Compile(literalPattern)
43-
if err != nil {
44-
return nil, fmt.Errorf(ottl.InvalidRegexErrMsg, "ExtractPatterns", literalPattern, err)
45-
}
46-
compiled = true
47-
}
48-
49-
if compiled {
50-
namedCaptureGroups := 0
51-
for _, groupName := range compiledPattern.SubexpNames() {
52-
if groupName != "" {
53-
namedCaptureGroups++
54-
}
55-
}
56-
57-
if namedCaptureGroups == 0 {
58-
return nil, errors.New("at least 1 named capture group must be supplied in the given regex")
59-
}
35+
compiledPattern, err := newDynamicRegex("ExtractPatterns", pattern)
36+
if err != nil {
37+
return nil, err
6038
}
6139

6240
return func(ctx context.Context, tCtx K) (any, error) {
63-
cp := compiledPattern
64-
if cp == nil {
65-
patternVal, err := pattern.Get(ctx, tCtx)
66-
if err != nil {
67-
return nil, err
68-
}
69-
cp, err = regexp.Compile(patternVal)
70-
if err != nil {
71-
return nil, fmt.Errorf(ottl.InvalidRegexErrMsg, "ExtractPatterns", patternVal, err)
72-
}
41+
cp, err := compiledPattern.compile(ctx, tCtx)
42+
if err != nil {
43+
return nil, err
7344
}
45+
7446
namedCaptureGroups := 0
7547
for _, groupName := range cp.SubexpNames() {
7648
if groupName != "" {

pkg/ottl/ottlfuncs/func_is_match.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@ package ottlfuncs // import "github.com/open-telemetry/opentelemetry-collector-c
66
import (
77
"context"
88
"errors"
9-
"fmt"
10-
"regexp"
119

1210
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
1311
)
1412

1513
type IsMatchArguments[K any] struct {
1614
Target ottl.StringLikeGetter[K]
17-
Pattern string
15+
Pattern ottl.StringGetter[K]
1816
}
1917

2018
func NewIsMatchFactory[K any]() ottl.Factory[K] {
@@ -31,19 +29,23 @@ func createIsMatchFunction[K any](_ ottl.FunctionContext, oArgs ottl.Arguments)
3129
return isMatch(args.Target, args.Pattern)
3230
}
3331

34-
func isMatch[K any](target ottl.StringLikeGetter[K], pattern string) (ottl.ExprFunc[K], error) {
35-
compiledPattern, err := regexp.Compile(pattern)
32+
func isMatch[K any](target ottl.StringLikeGetter[K], pattern ottl.StringGetter[K]) (ottl.ExprFunc[K], error) {
33+
compiledPattern, err := newDynamicRegex("IsMatch", pattern)
3634
if err != nil {
37-
return nil, fmt.Errorf("the pattern supplied to IsMatch is not a valid regexp pattern: %w", err)
35+
return nil, err
3836
}
3937
return func(ctx context.Context, tCtx K) (any, error) {
38+
cp, err := compiledPattern.compile(ctx, tCtx)
39+
if err != nil {
40+
return nil, err
41+
}
4042
val, err := target.Get(ctx, tCtx)
4143
if err != nil {
4244
return nil, err
4345
}
4446
if val == nil {
4547
return false, nil
4648
}
47-
return compiledPattern.MatchString(*val), nil
49+
return cp.MatchString(*val), nil
4850
}, nil
4951
}

pkg/ottl/ottlfuncs/func_is_match_test.go

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func Test_isMatch(t *testing.T) {
1818
tests := []struct {
1919
name string
2020
target ottl.StringLikeGetter[any]
21-
pattern string
21+
pattern ottl.StringGetter[any]
2222
expected bool
2323
}{
2424
{
@@ -28,7 +28,11 @@ func Test_isMatch(t *testing.T) {
2828
return "hello world", nil
2929
},
3030
},
31-
pattern: "hello.*",
31+
pattern: &ottl.StandardStringGetter[any]{
32+
Getter: func(_ context.Context, _ any) (any, error) {
33+
return "hello.*", nil
34+
},
35+
},
3236
expected: true,
3337
},
3438
{
@@ -38,7 +42,11 @@ func Test_isMatch(t *testing.T) {
3842
return "goodbye world", nil
3943
},
4044
},
41-
pattern: "hello.*",
45+
pattern: &ottl.StandardStringGetter[any]{
46+
Getter: func(_ context.Context, _ any) (any, error) {
47+
return "hello.*", nil
48+
},
49+
},
4250
expected: false,
4351
},
4452
{
@@ -48,7 +56,11 @@ func Test_isMatch(t *testing.T) {
4856
return "-12.001", nil
4957
},
5058
},
51-
pattern: "[-+]?\\d*\\.\\d+([eE][-+]?\\d+)?",
59+
pattern: &ottl.StandardStringGetter[any]{
60+
Getter: func(_ context.Context, _ any) (any, error) {
61+
return "[-+]?\\d*\\.\\d+([eE][-+]?\\d+)?", nil
62+
},
63+
},
5264
expected: true,
5365
},
5466
{
@@ -58,7 +70,11 @@ func Test_isMatch(t *testing.T) {
5870
return true, nil
5971
},
6072
},
61-
pattern: "true",
73+
pattern: &ottl.StandardStringGetter[any]{
74+
Getter: func(_ context.Context, _ any) (any, error) {
75+
return "true", nil
76+
},
77+
},
6278
expected: true,
6379
},
6480
{
@@ -68,7 +84,11 @@ func Test_isMatch(t *testing.T) {
6884
return int64(1), nil
6985
},
7086
},
71-
pattern: `\d`,
87+
pattern: &ottl.StandardStringGetter[any]{
88+
Getter: func(_ context.Context, _ any) (any, error) {
89+
return `\d`, nil
90+
},
91+
},
7292
expected: true,
7393
},
7494
{
@@ -78,7 +98,11 @@ func Test_isMatch(t *testing.T) {
7898
return 1.1, nil
7999
},
80100
},
81-
pattern: `\d\.\d`,
101+
pattern: &ottl.StandardStringGetter[any]{
102+
Getter: func(_ context.Context, _ any) (any, error) {
103+
return `\d\.\d`, nil
104+
},
105+
},
82106
expected: true,
83107
},
84108
{
@@ -90,7 +114,11 @@ func Test_isMatch(t *testing.T) {
90114
return v, nil
91115
},
92116
},
93-
pattern: `test`,
117+
pattern: &ottl.StandardStringGetter[any]{
118+
Getter: func(_ context.Context, _ any) (any, error) {
119+
return `test`, nil
120+
},
121+
},
94122
expected: true,
95123
},
96124
{
@@ -100,7 +128,11 @@ func Test_isMatch(t *testing.T) {
100128
return nil, nil
101129
},
102130
},
103-
pattern: "impossible to match",
131+
pattern: &ottl.StandardStringGetter[any]{
132+
Getter: func(_ context.Context, _ any) (any, error) {
133+
return "impossible to match", nil
134+
},
135+
},
104136
expected: false,
105137
},
106138
}
@@ -121,7 +153,14 @@ func Test_isMatch_validation(t *testing.T) {
121153
return "anything", nil
122154
},
123155
}
124-
_, err := isMatch[any](target, "\\K")
156+
invalidRegexPattern := ottl.StandardStringGetter[any]{
157+
Getter: func(_ context.Context, _ any) (any, error) {
158+
return "\\K", nil
159+
},
160+
}
161+
exprFunc, err := isMatch[any](target, invalidRegexPattern)
162+
assert.NoError(t, err)
163+
_, err = exprFunc(t.Context(), nil)
125164
require.Error(t, err)
126165
}
127166

@@ -131,7 +170,12 @@ func Test_isMatch_error(t *testing.T) {
131170
return make(chan int), nil
132171
},
133172
}
134-
exprFunc, err := isMatch[any](target, "test")
173+
regexPattern := ottl.StandardStringGetter[any]{
174+
Getter: func(_ context.Context, _ any) (any, error) {
175+
return "test", nil
176+
},
177+
}
178+
exprFunc, err := isMatch[any](target, regexPattern)
135179
assert.NoError(t, err)
136180
_, err = exprFunc(t.Context(), nil)
137181
require.Error(t, err)

pkg/ottl/ottlfuncs/func_replace_all_matches.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ func replaceAllMatches[K any](target ottl.PMapGetSetter[K], pattern, replacement
4747
if ok {
4848
compiledPattern, err = glob.Compile(literalPattern)
4949
if err != nil {
50-
return nil, fmt.Errorf(ottl.InvalidRegexErrMsg, "replace_all_matches", literalPattern, err)
50+
return nil, fmt.Errorf(invalidRegexErrMsg, "replace_all_matches", literalPattern, err)
5151
}
5252
}
53+
5354
return func(ctx context.Context, tCtx K) (any, error) {
5455
cp := compiledPattern
5556
if cp == nil {
@@ -59,7 +60,7 @@ func replaceAllMatches[K any](target ottl.PMapGetSetter[K], pattern, replacement
5960
}
6061
cp, err = glob.Compile(patternVal)
6162
if err != nil {
62-
return nil, fmt.Errorf(ottl.InvalidRegexErrMsg, "replace_all_matches", patternVal, err)
63+
return nil, fmt.Errorf(invalidRegexErrMsg, "replace_all_matches", patternVal, err)
6364
}
6465
}
6566
val, err := target.Get(ctx, tCtx)

0 commit comments

Comments
 (0)