Skip to content

Commit ef38bb6

Browse files
rnishtala-sumojmsnll
authored andcommitted
Add an optional argument to converters to support hashing (open-telemetry#27235)
**Description:** Functions to modify matched text during replacement can now be passed as optional arguments to the following Editors: - replace_pattern - replace_all_patterns - replace_match - replace_all_matches **Documentation:** https://github.com/rnishtala-sumo/opentelemetry-collector-contrib/blob/ottl-replace-pattern/pkg/ottl/ottlfuncs/README.md#replace_pattern **Issue:** Resolves open-telemetry#22787
1 parent 5d63b22 commit ef38bb6

13 files changed

+565
-70
lines changed

.chloggen/ottl-replace-pattern.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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. filelogreceiver)
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: Add optional Converter parameters to replacement Editors
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: [27235]
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+
Functions to modify matched text during replacement can now be passed as optional arguments to the following Editors:
20+
- `replace_pattern`
21+
- `replace_all_patterns`
22+
- `replace_match`
23+
- `replace_all_matches`
24+
25+
# If your change doesn't affect end users or the exported elements of any package,
26+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
27+
# Optional: The change log or logs in which this entry should be included.
28+
# e.g. '[user]' or '[user, api]'
29+
# Include 'user' if the change is relevant to end users.
30+
# Include 'api' if there is a change to a library API.
31+
# Default: '[user]'
32+
change_logs: [user]

pkg/ottl/expression.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -254,21 +254,21 @@ type FunctionGetter[K any] interface {
254254

255255
// StandardFunctionGetter is a basic implementation of FunctionGetter.
256256
type StandardFunctionGetter[K any] struct {
257-
fCtx FunctionContext
258-
fact Factory[K]
257+
FCtx FunctionContext
258+
Fact Factory[K]
259259
}
260260

261261
// Get takes an Arguments struct containing arguments the caller wants passed to the
262262
// function and instantiates the function with those arguments.
263263
// If there is a mismatch between the function's signature and the arguments the caller
264264
// wants to pass to the function, an error is returned.
265265
func (g StandardFunctionGetter[K]) Get(args Arguments) (Expr[K], error) {
266-
if g.fact == nil {
266+
if g.Fact == nil {
267267
return Expr[K]{}, fmt.Errorf("undefined function")
268268
}
269-
fArgs := g.fact.CreateDefaultArguments()
269+
fArgs := g.Fact.CreateDefaultArguments()
270270
if reflect.TypeOf(fArgs).Kind() != reflect.Pointer {
271-
return Expr[K]{}, fmt.Errorf("factory for %q must return a pointer to an Arguments value in its CreateDefaultArguments method", g.fact.Name())
271+
return Expr[K]{}, fmt.Errorf("factory for %q must return a pointer to an Arguments value in its CreateDefaultArguments method", g.Fact.Name())
272272
}
273273
if reflect.TypeOf(args).Kind() != reflect.Pointer {
274274
return Expr[K]{}, fmt.Errorf("%q must be pointer to an Arguments value", reflect.TypeOf(args).Kind())
@@ -282,7 +282,7 @@ func (g StandardFunctionGetter[K]) Get(args Arguments) (Expr[K], error) {
282282
field := argsVal.Field(i)
283283
fArgsVal.Field(i).Set(field)
284284
}
285-
fn, err := g.fact.CreateFunction(g.fCtx, fArgs)
285+
fn, err := g.Fact.CreateFunction(g.FCtx, fArgs)
286286
if err != nil {
287287
return Expr[K]{}, fmt.Errorf("couldn't create function: %w", err)
288288
}

pkg/ottl/expression_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,7 @@ func Test_FunctionGetter(t *testing.T) {
720720
return "str", nil
721721
},
722722
},
723-
function: StandardFunctionGetter[any]{fCtx: FunctionContext{Set: componenttest.NewNopTelemetrySettings()}, fact: functions["SHA256"]},
723+
function: StandardFunctionGetter[any]{FCtx: FunctionContext{Set: componenttest.NewNopTelemetrySettings()}, Fact: functions["SHA256"]},
724724
want: "anything",
725725
valid: true,
726726
},
@@ -731,7 +731,7 @@ func Test_FunctionGetter(t *testing.T) {
731731
return nil, nil
732732
},
733733
},
734-
function: StandardFunctionGetter[any]{fCtx: FunctionContext{Set: componenttest.NewNopTelemetrySettings()}, fact: functions["SHA250"]},
734+
function: StandardFunctionGetter[any]{FCtx: FunctionContext{Set: componenttest.NewNopTelemetrySettings()}, Fact: functions["SHA250"]},
735735
want: "anything",
736736
valid: false,
737737
expectedErrorMsg: "undefined function",
@@ -743,7 +743,7 @@ func Test_FunctionGetter(t *testing.T) {
743743
return nil, nil
744744
},
745745
},
746-
function: StandardFunctionGetter[any]{fCtx: FunctionContext{Set: componenttest.NewNopTelemetrySettings()}, fact: functions["test_arg_mismatch"]},
746+
function: StandardFunctionGetter[any]{FCtx: FunctionContext{Set: componenttest.NewNopTelemetrySettings()}, Fact: functions["test_arg_mismatch"]},
747747
want: "anything",
748748
valid: false,
749749
expectedErrorMsg: "incorrect number of arguments. Expected: 4 Received: 1",
@@ -755,7 +755,7 @@ func Test_FunctionGetter(t *testing.T) {
755755
return nil, nil
756756
},
757757
},
758-
function: StandardFunctionGetter[any]{fCtx: FunctionContext{Set: componenttest.NewNopTelemetrySettings()}, fact: functions["cannot_create_function"]},
758+
function: StandardFunctionGetter[any]{FCtx: FunctionContext{Set: componenttest.NewNopTelemetrySettings()}, Fact: functions["cannot_create_function"]},
759759
want: "anything",
760760
valid: false,
761761
expectedErrorMsg: "couldn't create function: error",

pkg/ottl/functions.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func (p *Parser[K]) buildArgs(ed editor, argsVal reflect.Value) error {
123123
if !ok {
124124
return fmt.Errorf("undefined function %s", name)
125125
}
126-
val = StandardFunctionGetter[K]{fCtx: FunctionContext{Set: p.telemetrySettings}, fact: f}
126+
val = StandardFunctionGetter[K]{FCtx: FunctionContext{Set: p.telemetrySettings}, Fact: f}
127127
case fieldType.Kind() == reflect.Slice:
128128
val, err = p.buildSliceArg(arg.Value, fieldType)
129129
default:

pkg/ottl/ottlfuncs/README.md

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Some functions are able to handle different types and will generally convert tho
1616
In these situations the function will error if it does not know how to do the conversion.
1717
Use `ErrorMode` to determine how the `Statement` handles these errors.
1818
See the component-specific guides for how each uses error mode:
19+
1920
- [filterprocessor](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/filterprocessor#ottl)
2021
- [routingprocessor](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/routingprocessor#tech-preview-opentelemetry-transformation-language-statements-as-routing-conditions)
2122
- [transformprocessor](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/transformprocessor#config)
@@ -26,11 +27,12 @@ Editors are what OTTL uses to transform telemetry.
2627

2728
Editors:
2829

29-
- Are allowed to transform telemetry. When a Function is invoked the expectation is that the underlying telemetry is modified in some way.
30-
- May have side effects. Some Functions may generate telemetry and add it to the telemetry payload to be processed in this batch.
31-
- May return values. Although not common and not required, Functions may return values.
30+
- Are allowed to transform telemetry. When a Function is invoked the expectation is that the underlying telemetry is modified in some way.
31+
- May have side effects. Some Functions may generate telemetry and add it to the telemetry payload to be processed in this batch.
32+
- May return values. Although not common and not required, Functions may return values.
3233

3334
Available Editors:
35+
3436
- [delete_key](#delete_key)
3537
- [delete_matching_keys](#delete_matching_keys)
3638
- [keep_keys](#keep_keys)
@@ -55,8 +57,8 @@ The key will be deleted from the map.
5557

5658
Examples:
5759

58-
- `delete_key(attributes, "http.request.header.authorization")`
5960

61+
- `delete_key(attributes, "http.request.header.authorization")`
6062

6163
- `delete_key(resource.attributes, "http.request.header.authorization")`
6264

@@ -72,8 +74,8 @@ All keys that match the pattern will be deleted from the map.
7274

7375
Examples:
7476

75-
- `delete_key(attributes, "http.request.header.authorization")`
7677

78+
- `delete_key(attributes, "http.request.header.authorization")`
7779

7880
- `delete_key(resource.attributes, "http.request.header.authorization")`
7981

@@ -126,6 +128,7 @@ The `merge_maps` function merges the source map into the target map using the su
126128
`target` is a `pdata.Map` type field. `source` is a `pdata.Map` type field. `strategy` is a string that must be one of `insert`, `update`, or `upsert`.
127129

128130
If strategy is:
131+
129132
- `insert`: Insert the value from `source` into `target` where the key does not already exist.
130133
- `update`: Update the entry in `target` with the value from `source` where the key does exist.
131134
- `upsert`: Performs insert or update. Insert the value from `source` into `target` where the key does not already exist and update the entry in `target` with the value from `source` where the key does exist.
@@ -144,11 +147,11 @@ Examples:
144147

145148
### replace_all_matches
146149

147-
`replace_all_matches(target, pattern, replacement)`
150+
`replace_all_matches(target, pattern, replacement, function)`
148151

149152
The `replace_all_matches` function replaces any matching string value with the replacement string.
150153

151-
`target` is a path expression to a `pdata.Map` type field. `pattern` is a string following [filepath.Match syntax](https://pkg.go.dev/path/filepath#Match). `replacement` is either a path expression to a string telemetry field or a literal string.
154+
`target` is a path expression to a `pdata.Map` type field. `pattern` is a string following [filepath.Match syntax](https://pkg.go.dev/path/filepath#Match). `replacement` is either a path expression to a string telemetry field or a literal string. `function` is an optional argument that can take in any Converter that accepts a (`replacement`) string and returns a string. An example is a hash function that replaces any matching string with the hash value of `replacement`.
152155

153156
Each string value in `target` that matches `pattern` will get replaced with `replacement`. Non-string values are ignored.
154157

@@ -158,10 +161,11 @@ There is currently a bug with OTTL that does not allow the pattern to end with `
158161
Examples:
159162

160163
- `replace_all_matches(attributes, "/user/*/list/*", "/user/{userId}/list/{listId}")`
164+
- `replace_all_matches(attributes, "/user/*/list/*", "/user/{userId}/list/{listId}", SHA256)`
161165

162166
### replace_all_patterns
163167

164-
`replace_all_patterns(target, mode, regex, replacement)`
168+
`replace_all_patterns(target, mode, regex, replacement, function)`
165169

166170
The `replace_all_patterns` function replaces any segments in a string value or key that match the regex pattern with the replacement string.
167171

@@ -173,6 +177,8 @@ If one or more sections of `target` match `regex` they will get replaced with `r
173177

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

180+
The `function` is an optional argument that can take in any Converter that accepts a (`replacement`) string and returns a string. An example is a hash function that replaces any matching regex pattern with the hash value of `replacement`.
181+
176182
There is currently a bug with OTTL that does not allow the pattern to end with `\\"`.
177183
If your pattern needs to end with backslashes, add something inconsequential to the end of the pattern such as `{1}`, `$`, or `.*`.
178184
[See Issue 23238 for details](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/23238).
@@ -182,31 +188,35 @@ Examples:
182188
- `replace_all_patterns(attributes, "value", "/account/\\d{4}", "/account/{accountId}")`
183189
- `replace_all_patterns(attributes, "key", "/account/\\d{4}", "/account/{accountId}")`
184190
- `replace_all_patterns(attributes, "key", "^kube_([0-9A-Za-z]+_)", "k8s.$$1.")`
191+
- `replace_all_patterns(attributes, "key", "^kube_([0-9A-Za-z]+_)", "k8s.$$1.", SHA256)`
185192

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

190197
### replace_match
191198

192-
`replace_match(target, pattern, replacement)`
199+
`replace_match(target, pattern, replacement, function)`
193200

194201
The `replace_match` function allows replacing entire strings if they match a glob pattern.
195202

196203
`target` is a path expression to a telemetry field. `pattern` is a string following [filepath.Match syntax](https://pkg.go.dev/path/filepath#Match). `replacement` is either a path expression to a string telemetry field or a literal string.
197204

198205
If `target` matches `pattern` it will get replaced with `replacement`.
199206

207+
The `function` is an optional argument that can take in any Converter that accepts a (`replacement`) string and returns a string. An example is a hash function that replaces any matching glob pattern with the hash value of `replacement`.
208+
200209
There is currently a bug with OTTL that does not allow the pattern to end with `\\"`.
201210
[See Issue 23238 for details](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/23238).
202211

203212
Examples:
204213

205214
- `replace_match(attributes["http.target"], "/user/*/list/*", "/user/{userId}/list/{listId}")`
215+
- `replace_match(attributes["http.target"], "/user/*/list/*", "/user/{userId}/list/{listId}", SHA256)`
206216

207217
### replace_pattern
208218

209-
`replace_pattern(target, regex, replacement)`
219+
`replace_pattern(target, regex, replacement, function)`
210220

211221
The `replace_pattern` function allows replacing all string sections that match a regex pattern with a new value.
212222

@@ -216,6 +226,8 @@ If one or more sections of `target` match `regex` they will get replaced with `r
216226

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

229+
The `function` is an optional argument that can take in any Converter that accepts a (`replacement`) string and returns a string. An example is a hash function that replaces a matching regex pattern with the hash value of `replacement`.
230+
219231
There is currently a bug with OTTL that does not allow the pattern to end with `\\"`.
220232
If your pattern needs to end with backslashes, add something inconsequential to the end of the pattern such as `{1}`, `$`, or `.*`.
221233
[See Issue 23238 for details](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/23238).
@@ -224,6 +236,7 @@ Examples:
224236

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

228241
Note that when using OTTL within the collector's configuration file, `$` must be escaped to `$$` to bypass
229242
environment variable substitution logic. To input a literal `$` from the configuration file, use `$$$`.
@@ -275,6 +288,7 @@ Converters are pure functions that take OTTL values as input and output a single
275288
Unlike functions, they do not modify any input telemetry and always return a value.
276289

277290
Available Converters:
291+
278292
- [Concat](#concat)
279293
- [ConvertCase](#convertcase)
280294
- [ExtractPatterns](#extractpatterns)
@@ -373,9 +387,9 @@ Examples:
373387

374388
The `ExtractPatterns` Converter returns a `pcommon.Map` struct that is a result of extracting named capture groups from the target string. If not matches are found then an empty `pcommon.Map` is returned.
375389

376-
`target` is a Getter that returns a string. `pattern` is a regex string.
390+
`target` is a Getter that returns a string. `pattern` is a regex string.
377391

378-
If `target` is not a string or nil `ExtractPatterns` will return an error. If `pattern` does not contain at least 1 named capture group then `ExtractPatterns` will error on startup.
392+
If `target` is not a string or nil `ExtractPatterns` will return an error. If `pattern` does not contain at least 1 named capture group then `ExtractPatterns` will error on startup.
379393

380394
Examples:
381395

@@ -425,10 +439,11 @@ The `Int` Converter converts the `value` to int type.
425439
The returned type is int64.
426440

427441
The input `value` types:
428-
* float64. Fraction is discharged (truncation towards zero).
429-
* string. Trying to parse an integer from string if it fails then nil will be returned.
430-
* bool. If `value` is true, then the function will return 1 otherwise 0.
431-
* int64. The function returns the `value` without changes.
442+
443+
- float64. Fraction is discharged (truncation towards zero).
444+
- string. Trying to parse an integer from string if it fails then nil will be returned.
445+
- bool. If `value` is true, then the function will return 1 otherwise 0.
446+
- int64. The function returns the `value` without changes.
432447

433448
If `value` is another type or parsing failed nil is always returned.
434449

@@ -531,8 +546,8 @@ If target is not a float64, it will be converted to one:
531546

532547
- int64s are converted to float64s
533548
- strings are converted using `strconv`
534-
- booleans are converted using `1` for `true` and `0` for `false`. This means passing `false` to the function will cause an error.
535-
- int, float, string, and bool OTLP Values are converted following the above rules depending on their type. Other types cause an error.
549+
- booleans are converted using `1` for `true` and `0` for `false`. This means passing `false` to the function will cause an error.
550+
- int, float, string, and bool OTLP Values are converted following the above rules depending on their type. Other types cause an error.
536551

537552
If target is nil an error is returned.
538553

@@ -713,7 +728,7 @@ Examples:
713728

714729
### Split
715730

716-
`Split(target, delimiter)`
731+
```Split(target, delimiter)```
717732

718733
The `Split` Converter separates a string by the delimiter, and returns an array of substrings.
719734

@@ -726,7 +741,7 @@ There is currently a bug with OTTL that does not allow the target string to end
726741

727742
Examples:
728743

729-
- ```Split("A|B|C", "|")```
744+
- `Split("A|B|C", "|")`
730745

731746
### Substring
732747

@@ -773,7 +788,7 @@ Examples:
773788

774789
The `TruncateTime` Converter returns the given time rounded down to a multiple of the given duration. The Converter [uses the `time.Truncate` function](https://pkg.go.dev/time#Time.Truncate).
775790

776-
`time` is a `time.Time`. `duration` is a `time.Duration`. If `time` is not a `time.Time` or if `duration` is not a `time.Duration`, an error will be returned.
791+
`time` is a `time.Time`. `duration` is a `time.Duration`. If `time` is not a `time.Time` or if `duration` is not a `time.Duration`, an error will be returned.
777792

778793
While some common paths can return a `time.Time` object, you will most like need to use the [Duration Converter](#duration) to create a `time.Duration`.
779794

@@ -846,9 +861,10 @@ The `UUID` function generates a v4 uuid string.
846861
## Function syntax
847862

848863
Functions should be named and formatted according to the following standards.
864+
849865
- Function names MUST start with a verb unless it is a Factory that creates a new type.
850866
- Converters MUST be UpperCamelCase.
851867
- Function names that contain multiple words MUST separate those words with `_`.
852-
- Functions that interact with multiple items MUST have plurality in the name. Ex: `truncate_all`, `keep_keys`, `replace_all_matches`.
853-
- Functions that interact with a single item MUST NOT have plurality in the name. If a function would interact with multiple items due to a condition, like `where`, it is still considered singular. Ex: `set`, `delete`, `replace_match`.
868+
- Functions that interact with multiple items MUST have plurality in the name. Ex: `truncate_all`, `keep_keys`, `replace_all_matches`.
869+
- Functions that interact with a single item MUST NOT have plurality in the name. If a function would interact with multiple items due to a condition, like `where`, it is still considered singular. Ex: `set`, `delete`, `replace_match`.
854870
- Functions that change a specific target MUST set the target as the first parameter.

0 commit comments

Comments
 (0)