Skip to content

Commit

Permalink
[pkg/ottl] Support referencing matched capture groups in replace_patt…
Browse files Browse the repository at this point in the history
…ern (#18614)

Use ReplaceAllString() instead of ReplaceAllLiteralString() to allow referencing matched capture groups in replacement

Link to tracking Issue:
#18610

Co-authored-by: Evan Bradley <[email protected]>
  • Loading branch information
sirianni and evan-bradley authored Feb 23, 2023
1 parent 7a6c53c commit 13ca52d
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 3 deletions.
19 changes: 19 additions & 0 deletions .chloggen/ottl-replace-capture-groups.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 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: Ability to reference matched capture groups in `replace_pattern()` and `replace_all_patterns()`

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

# (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: |
This change affects all processors that use OTTL (i.e. `transformprocessor`, `routingprocessor`, and `filterprocessor`).
This is a breaking change in the rare scenario that the `$` character is currently used in a replacement string.
To output a literal `$` in a replacement string it must now be escaped with an additional `$`.
15 changes: 14 additions & 1 deletion pkg/ottl/ottlfuncs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ They manipulate the OTTL grammar value into a form that will make working with t

Converters:
- Are pure functions. They should never change the underlying telemetry and the same inputs should always result in the same output.
- Always return something.
- Always return something.

List of available Converters:
- [Concat](#concat)
Expand Down Expand Up @@ -338,10 +338,17 @@ The `replace_all_patterns` function replaces any segments in a string value or k

If one or more sections of `target` match `regex` they will get replaced with `replacement`.

The `replacement` string can refer to matched groups using [regexp.Expand syntax](https://pkg.go.dev/regexp#Regexp.Expand).

Examples:

- `replace_all_patterns(attributes, "value", "/account/\\d{4}", "/account/{accountId}")`
- `replace_all_patterns(attributes, "key", "/account/\\d{4}", "/account/{accountId}")`
- `replace_all_patterns(attributes, "key", "^kube_([0-9A-Za-z]+_)", "k8s.$$1.")`

Note that when using OTTL within the collector's configuration file, `$` must be escaped to `$$` to bypass
environment variable substitution logic. To input a literal `$` from the configuration file, use `$$$`.
If using OTTL outside of collector configuration, `$` should not be escaped and a literal `$` can be entered using `$$`.

### replace_pattern

Expand All @@ -353,10 +360,16 @@ The `replace_pattern` function allows replacing all string sections that match a

If one or more sections of `target` match `regex` they will get replaced with `replacement`.

The `replacement` string can refer to matched groups using [regexp.Expand syntax](https://pkg.go.dev/regexp#Regexp.Expand).

Examples:

- `replace_pattern(resource.attributes["process.command_line"], "password\\=[^\\s]*(\\s?)", "password=***")`
- `replace_pattern(name, "^kube_([0-9A-Za-z]+_)", "k8s.$$1.")`

Note that when using OTTL within the collector's configuration file, `$` must be escaped to `$$` to bypass
environment variable substitution logic. To input a literal `$` from the configuration file, use `$$$`.
If using OTTL outside of collector configuration, `$` should not be escaped and a literal `$` can be entered using `$$`.

### replace_match

Expand Down
2 changes: 1 addition & 1 deletion pkg/ottl/ottlfuncs/func_replace_all_patterns.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func ReplaceAllPatterns[K any](target ottl.GetSetter[K], mode string, regexPatte
switch mode {
case modeValue:
if compiledPattern.MatchString(originalValue.Str()) {
updatedString := compiledPattern.ReplaceAllLiteralString(originalValue.Str(), replacement)
updatedString := compiledPattern.ReplaceAllString(originalValue.Str(), replacement)
updated.PutStr(key, updatedString)
} else {
updated.PutStr(key, originalValue.Str())
Expand Down
26 changes: 26 additions & 0 deletions pkg/ottl/ottlfuncs/func_replace_all_patterns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,32 @@ func Test_replaceAllPatterns(t *testing.T) {
expectedMap.PutStr("test.3", "goodbye world1 and world2")
},
},
{
name: "expand capturing groups",
target: target,
mode: modeValue,
pattern: `world(\d)`,
replacement: "world-$1",
want: func(expectedMap pcommon.Map) {
expectedMap.Clear()
expectedMap.PutStr("test", "hello world")
expectedMap.PutStr("test2", "hello")
expectedMap.PutStr("test3", "goodbye world-1 and world-2")
},
},
{
name: "replacement with literal $",
target: target,
mode: modeValue,
pattern: `world(\d)`,
replacement: "$$world-$1",
want: func(expectedMap pcommon.Map) {
expectedMap.Clear()
expectedMap.PutStr("test", "hello world")
expectedMap.PutStr("test2", "hello")
expectedMap.PutStr("test3", "goodbye $world-1 and $world-2")
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/ottl/ottlfuncs/func_replace_pattern.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func ReplacePattern[K any](target ottl.GetSetter[K], regexPattern string, replac
}
if originalValStr, ok := originalVal.(string); ok {
if compiledPattern.MatchString(originalValStr) {
updatedStr := compiledPattern.ReplaceAllLiteralString(originalValStr, replacement)
updatedStr := compiledPattern.ReplaceAllString(originalValStr, replacement)
err = target.Set(ctx, tCtx, updatedStr)
if err != nil {
return nil, err
Expand Down
18 changes: 18 additions & 0 deletions pkg/ottl/ottlfuncs/func_replace_pattern_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ func Test_replacePattern(t *testing.T) {
expectedValue.SetStr("application passwd=sensitivedtata otherarg=notsensitive **** **** ")
},
},
{
name: "expand capturing groups",
target: target,
pattern: `(\w+)=(\w+)`,
replacement: "$1:$2",
want: func(expectedValue pcommon.Value) {
expectedValue.SetStr("application passwd:sensitivedtata otherarg:notsensitive key1 key2")
},
},
{
name: "replacement with literal $",
target: target,
pattern: `passwd\=[^\s]*(\s?)`,
replacement: "passwd=$$$$$$ ",
want: func(expectedValue pcommon.Value) {
expectedValue.SetStr("application passwd=$$$ otherarg=notsensitive key1 key2")
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down

0 comments on commit 13ca52d

Please sign in to comment.