Skip to content

Commit a4b0e59

Browse files
authored
[processor/transform] Add common where clause (#31491)
**Description:** Add global conditions with where clause <!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> **Link to tracking Issue:** Fixes #27830 **Testing:** Unit tests **Documentation:** TODO ~~The main objective is to extend the `ContextStatements` struct by adding a new `Conditions` parameter. By introducing `Conditions` to `ContextStatements`, we can now apply a global condition to all related statements in `WithStatementSequenceGlobalConditions` function.~~ Thanks in advance for your feedback! If this changes will be fine, I will add common where clause into another context `span`, `metrics`.
1 parent c0512b9 commit a4b0e59

File tree

16 files changed

+923
-25
lines changed

16 files changed

+923
-25
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. filelogreceiver)
7+
component: processor/transform
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Allow common where clause
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: [27830]
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: [user]

internal/filter/expr/matcher.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ func Not[K any](matcher BoolExpr[K]) BoolExpr[K] {
2525
return notMatcher[K]{matcher: matcher}
2626
}
2727

28+
type alwaysTrueMatcher[K any] struct{}
29+
30+
func (alm alwaysTrueMatcher[K]) Eval(_ context.Context, _ K) (bool, error) {
31+
return true, nil
32+
}
33+
34+
func AlwaysTrue[K any]() BoolExpr[K] {
35+
return alwaysTrueMatcher[K]{}
36+
}
37+
2838
type orMatcher[K any] struct {
2939
matchers []BoolExpr[K]
3040
}

internal/filter/filterottl/filter.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottllog"
1313
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric"
1414
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlresource"
15+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlscope"
1516
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan"
1617
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspanevent"
1718
)
@@ -111,3 +112,19 @@ func NewBoolExprForResource(conditions []string, functions map[string]ottl.Facto
111112
c := ottlresource.NewConditionSequence(statements, set, ottlresource.WithConditionSequenceErrorMode(errorMode))
112113
return &c, nil
113114
}
115+
116+
// NewBoolExprForScope creates a BoolExpr[ottlscope.TransformContext] that will return true if any of the given OTTL conditions evaluate to true.
117+
// The passed in functions should use the ottlresource.TransformContext.
118+
// If a function named `match` is not present in the function map it will be added automatically so that parsing works as expected
119+
func NewBoolExprForScope(conditions []string, functions map[string]ottl.Factory[ottlscope.TransformContext], errorMode ottl.ErrorMode, set component.TelemetrySettings) (expr.BoolExpr[ottlscope.TransformContext], error) {
120+
parser, err := ottlscope.NewParser(functions, set)
121+
if err != nil {
122+
return nil, err
123+
}
124+
statements, err := parser.ParseConditions(conditions)
125+
if err != nil {
126+
return nil, err
127+
}
128+
c := ottlscope.NewConditionSequence(statements, set, ottlscope.WithConditionSequenceErrorMode(errorMode))
129+
return &c, nil
130+
}

internal/filter/filterottl/filter_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottllog"
1616
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric"
1717
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlresource"
18+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlscope"
1819
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan"
1920
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspanevent"
2021
)
@@ -270,3 +271,53 @@ func Test_NewBoolExprForResource(t *testing.T) {
270271
})
271272
}
272273
}
274+
275+
func Test_NewBoolExprForScope(t *testing.T) {
276+
tests := []struct {
277+
name string
278+
conditions []string
279+
expectedResult bool
280+
}{
281+
{
282+
name: "basic",
283+
conditions: []string{
284+
"true == true",
285+
},
286+
expectedResult: true,
287+
},
288+
{
289+
name: "multiple conditions resulting true",
290+
conditions: []string{
291+
"false == true",
292+
"true == true",
293+
},
294+
expectedResult: true,
295+
},
296+
{
297+
name: "multiple conditions resulting false",
298+
conditions: []string{
299+
"false == true",
300+
"true == false",
301+
},
302+
expectedResult: false,
303+
},
304+
{
305+
name: "With Converter",
306+
conditions: []string{
307+
`IsMatch("test", "pass")`,
308+
},
309+
expectedResult: false,
310+
},
311+
}
312+
313+
for _, tt := range tests {
314+
t.Run(tt.name, func(t *testing.T) {
315+
resBoolExpr, err := NewBoolExprForScope(tt.conditions, StandardScopeFuncs(), ottl.PropagateError, componenttest.NewNopTelemetrySettings())
316+
assert.NoError(t, err)
317+
assert.NotNil(t, resBoolExpr)
318+
result, err := resBoolExpr.Eval(context.Background(), ottlscope.TransformContext{})
319+
assert.NoError(t, err)
320+
assert.Equal(t, tt.expectedResult, result)
321+
})
322+
}
323+
}

internal/filter/filterottl/functions.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottllog"
1515
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric"
1616
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlresource"
17+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlscope"
1718
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan"
1819
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspanevent"
1920
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs"
@@ -40,6 +41,10 @@ func StandardDataPointFuncs() map[string]ottl.Factory[ottldatapoint.TransformCon
4041
return ottlfuncs.StandardConverters[ottldatapoint.TransformContext]()
4142
}
4243

44+
func StandardScopeFuncs() map[string]ottl.Factory[ottlscope.TransformContext] {
45+
return ottlfuncs.StandardConverters[ottlscope.TransformContext]()
46+
}
47+
4348
func StandardLogFuncs() map[string]ottl.Factory[ottllog.TransformContext] {
4449
return ottlfuncs.StandardConverters[ottllog.TransformContext]()
4550
}

processor/transformprocessor/README.md

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515
The transform processor modifies telemetry based on configuration using the [OpenTelemetry Transformation Language](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/pkg/ottl).
1616

17-
For each signal type, the processor takes a list of statements associated to a [Context type](#contexts) and executes the statements against the incoming telemetry in the order specified in the config.
18-
Each statement can access and transform telemetry using functions and allow the use of a condition to help decide whether the function should be executed.
17+
For each signal type, the processor takes a list of conditions and statements associated to a [Context type](#contexts) and executes the conditions and statements against the incoming telemetry in the order specified in the config.
18+
Each condition and statement can access and transform telemetry using functions and allow the use of a condition to help decide whether the function should be executed.
1919

2020
- [Config](#config)
2121
- [Grammar](#grammar)
@@ -28,8 +28,8 @@ Each statement can access and transform telemetry using functions and allow the
2828

2929
The transform processor allows configuring multiple context statements for traces, metrics, and logs.
3030
The value of `context` specifies which [OTTL Context](#contexts) to use when interpreting the associated statements.
31-
The statement strings, which must be OTTL compatible, will be passed to the OTTL and interpreted using the associated context.
32-
Each context will be processed in the order specified and each statement for a context will be executed in the order specified.
31+
The conditions and statement strings, which must be OTTL compatible, will be passed to the OTTL and interpreted using the associated context. The conditions string should contain a string with a WHERE clause body without the `where` keyword at the beginning.
32+
Each context will be processed in the order specified and each condition and statement for a context will be executed in the order specified. Conditions are executed first, if a context doesn't meet the conditions, the associated statement will be skipped.
3333

3434
The transform processor also allows configuring an optional field, `error_mode`, which will determine how the processor reacts to errors that occur while processing a statement.
3535

@@ -46,6 +46,9 @@ transform:
4646
error_mode: ignore
4747
<trace|metric|log>_statements:
4848
- context: string
49+
conditions:
50+
- string
51+
- string
4952
statements:
5053
- string
5154
- string
@@ -67,6 +70,27 @@ Valid values for `context` are:
6770
| metric_statements | `resource`, `scope`, `metric`, and `datapoint` |
6871
| log_statements | `resource`, `scope`, and `log` |
6972

73+
`conditions` is a list comprised of multiple where clauses, which will be processed as global conditions for the accompanying set of statements.
74+
75+
```yaml
76+
transform:
77+
error_mode: ignore
78+
metric_statements:
79+
- context: metric
80+
conditions:
81+
- type == METRIC_DATA_TYPE_SUM
82+
statements:
83+
- set(description, "Sum")
84+
85+
log_statements:
86+
- context: log
87+
conditions:
88+
- IsMap(body) and body["object"] != nil
89+
statements:
90+
- set(body, attributes["http.route"])
91+
```
92+
93+
7094
### Example
7195

7296
The example takes advantage of context efficiency by grouping transformations with the context which it intends to transform.

processor/transformprocessor/config_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,39 @@ func TestLoadConfig(t *testing.T) {
7676
},
7777
},
7878
},
79+
{
80+
id: component.NewIDWithName(metadata.Type, "with_conditions"),
81+
expected: &Config{
82+
ErrorMode: ottl.PropagateError,
83+
TraceStatements: []common.ContextStatements{
84+
{
85+
Context: "span",
86+
Conditions: []string{`attributes["http.path"] == "/animal"`},
87+
Statements: []string{
88+
`set(name, "bear")`,
89+
},
90+
},
91+
},
92+
MetricStatements: []common.ContextStatements{
93+
{
94+
Context: "datapoint",
95+
Conditions: []string{`attributes["http.path"] == "/animal"`},
96+
Statements: []string{
97+
`set(metric.name, "bear")`,
98+
},
99+
},
100+
},
101+
LogStatements: []common.ContextStatements{
102+
{
103+
Context: "log",
104+
Conditions: []string{`attributes["http.path"] == "/animal"`},
105+
Statements: []string{
106+
`set(body, "bear")`,
107+
},
108+
},
109+
},
110+
},
111+
},
79112
{
80113
id: component.NewIDWithName(metadata.Type, "ignore_errors"),
81114
expected: &Config{

0 commit comments

Comments
 (0)