Skip to content

Commit a69efa1

Browse files
rockdabootedmocostaTylerHelmuth
authored
[processor/transform] Add support for profiles (#39974)
#### Description PR for profiles support in the transform processor. It's a split-out from #39036 and contains only basic functionality, that makes this PR independent from #39681, #39416 and open-telemetry/opentelemetry-collector#12798. For this, several tests were commented out. The reason for this PR is the hope to get it merged so that users and developers can start experimenting with profiles functionality in the transformprocessor. Attributes can't currently can't be used with profiles (due to the above mentioned unmerged PRs). #### Link to tracking issue Fixes #39009 --------- Co-authored-by: Edmo Vamerlatti Costa <[email protected]> Co-authored-by: Tyler Helmuth <[email protected]>
1 parent 07f58dc commit a69efa1

File tree

18 files changed

+2116
-45
lines changed

18 files changed

+2116
-45
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: transformprocessor
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add profiles support to transformprocessor.
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: [39009]
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]

processor/transformprocessor/README.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,20 @@ transform:
4646
<trace|metric|log>_statements: []
4747
```
4848
49-
The Transform Processor's primary configuration section is broken down by signal (traces, metrics, and logs)
49+
The Transform Processor's primary configuration section is broken down by signal (traces, metrics, logs, and profiles)
5050
and allows you to configure a list of statements for the processor to execute. The list can be made of:
5151
5252
- OTTL statements. This option will meet most user's needs. See [Basic Config](#basic-config) for more details.
5353
- Objects, which allows users to apply configuration options to a specific list of statements. See [Advanced Config](#advanced-config) for more details.
5454
5555
Within each `<signal_statements>` list, only certain OTTL Path prefixes can be used:
5656

57-
| Signal | Path Prefix Values |
58-
|-------------------|------------------------------------------------|
59-
| trace_statements | `resource`, `scope`, `span`, and `spanevent` |
60-
| metric_statements | `resource`, `scope`, `metric`, and `datapoint` |
61-
| log_statements | `resource`, `scope`, and `log` |
57+
| Signal | Path Prefix Values |
58+
|--------------------|------------------------------------------------|
59+
| trace_statements | `resource`, `scope`, `span`, and `spanevent` |
60+
| metric_statements | `resource`, `scope`, `metric`, and `datapoint` |
61+
| log_statements | `resource`, `scope`, and `log` |
62+
| profile_statements | `resource`, `scope`, and `profile` |
6263

6364
This means, for example, that you cannot use the Path `span.attributes` within the `log_statements` configuration section.
6465

@@ -114,6 +115,10 @@ transform:
114115
- replace_all_matches(log.attributes, "/user/*/list/*", "/user/{userId}/list/{listId}")
115116
- replace_all_patterns(log.attributes, "value", "/account/\\d{4}", "/account/{accountId}")
116117
- set(log.body, log.attributes["http.route"])
118+
profile_statements:
119+
- keep_keys(resource.attributes, ["host.name"])
120+
- set(profile.attributes["tag"], "profile#23")
121+
- set(profile.original_payload_format, "json")
117122
```
118123

119124
In some situations a combination of Paths, functions, or enums is not allowed, and the solution
@@ -133,7 +138,7 @@ Format:
133138
```yaml
134139
transform:
135140
error_mode: ignore
136-
<trace|metric|log>_statements:
141+
<trace|metric|log|profile>_statements:
137142
- context: string
138143
error_mode: propagate
139144
conditions:

processor/transformprocessor/config.go

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottldatapoint"
1919
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottllog"
2020
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric"
21+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlprofile"
2122
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan"
2223
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspanevent"
2324
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor/internal/common"
@@ -41,9 +42,10 @@ type Config struct {
4142
// The default value is `propagate`.
4243
ErrorMode ottl.ErrorMode `mapstructure:"error_mode"`
4344

44-
TraceStatements []common.ContextStatements `mapstructure:"trace_statements"`
45-
MetricStatements []common.ContextStatements `mapstructure:"metric_statements"`
46-
LogStatements []common.ContextStatements `mapstructure:"log_statements"`
45+
TraceStatements []common.ContextStatements `mapstructure:"trace_statements"`
46+
MetricStatements []common.ContextStatements `mapstructure:"metric_statements"`
47+
LogStatements []common.ContextStatements `mapstructure:"log_statements"`
48+
ProfileStatements []common.ContextStatements `mapstructure:"profile_statements"`
4749

4850
FlattenData bool `mapstructure:"flatten_data"`
4951
logger *zap.Logger
@@ -53,6 +55,7 @@ type Config struct {
5355
metricFunctions map[string]ottl.Factory[ottlmetric.TransformContext]
5456
spanEventFunctions map[string]ottl.Factory[ottlspanevent.TransformContext]
5557
spanFunctions map[string]ottl.Factory[ottlspan.TransformContext]
58+
profileFunctions map[string]ottl.Factory[ottlprofile.TransformContext]
5659
}
5760

5861
// Unmarshal is used internally by mapstructure to parse the transformprocessor configuration (Config),
@@ -80,9 +83,10 @@ func (c *Config) Unmarshal(conf *confmap.Conf) error {
8083
}
8184

8285
contextStatementsFields := map[string]*[]common.ContextStatements{
83-
"trace_statements": &c.TraceStatements,
84-
"metric_statements": &c.MetricStatements,
85-
"log_statements": &c.LogStatements,
86+
"trace_statements": &c.TraceStatements,
87+
"metric_statements": &c.MetricStatements,
88+
"log_statements": &c.LogStatements,
89+
"profile_statements": &c.ProfileStatements,
8690
}
8791

8892
contextStatementsPatch := map[string]any{}
@@ -179,6 +183,19 @@ func (c *Config) Validate() error {
179183
}
180184
}
181185

186+
if len(c.ProfileStatements) > 0 {
187+
pc, err := common.NewProfileParserCollection(component.TelemetrySettings{Logger: zap.NewNop()}, common.WithProfileParser(c.profileFunctions))
188+
if err != nil {
189+
return err
190+
}
191+
for _, cs := range c.ProfileStatements {
192+
_, err = pc.ParseContextStatements(cs)
193+
if err != nil {
194+
errors = multierr.Append(errors, err)
195+
}
196+
}
197+
}
198+
182199
if c.FlattenData && !flatLogsFeatureGate.IsEnabled() {
183200
errors = multierr.Append(errors, errFlatLogsGateDisabled)
184201
}

processor/transformprocessor/config_test.go

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,20 @@ func TestLoadConfig(t *testing.T) {
7575
},
7676
},
7777
},
78+
ProfileStatements: []common.ContextStatements{
79+
{
80+
Context: "profile",
81+
Statements: []string{
82+
`set(original_payload_format, "bear") where original_payload_format == "/animal"`,
83+
},
84+
},
85+
{
86+
Context: "resource",
87+
Statements: []string{
88+
`set(attributes["name"], "bear")`,
89+
},
90+
},
91+
},
7892
},
7993
},
8094
{
@@ -108,6 +122,15 @@ func TestLoadConfig(t *testing.T) {
108122
},
109123
},
110124
},
125+
ProfileStatements: []common.ContextStatements{
126+
{
127+
Context: "profile",
128+
Conditions: []string{`original_payload_format == "/animal"`},
129+
Statements: []string{
130+
`set(original_payload_format, "bear")`,
131+
},
132+
},
133+
},
111134
},
112135
},
113136
{
@@ -122,8 +145,9 @@ func TestLoadConfig(t *testing.T) {
122145
},
123146
},
124147
},
125-
MetricStatements: []common.ContextStatements{},
126-
LogStatements: []common.ContextStatements{},
148+
MetricStatements: []common.ContextStatements{},
149+
LogStatements: []common.ContextStatements{},
150+
ProfileStatements: []common.ContextStatements{},
127151
},
128152
},
129153
{
@@ -144,6 +168,12 @@ func TestLoadConfig(t *testing.T) {
144168
{
145169
id: component.NewIDWithName(metadata.Type, "unknown_function_log"),
146170
},
171+
{
172+
id: component.NewIDWithName(metadata.Type, "bad_syntax_profile"),
173+
},
174+
{
175+
id: component.NewIDWithName(metadata.Type, "unknown_function_profile"),
176+
},
147177
{
148178
id: component.NewIDWithName(metadata.Type, "bad_syntax_multi_signal"),
149179
errors: []error{
@@ -174,6 +204,12 @@ func TestLoadConfig(t *testing.T) {
174204
Statements: []string{`set(log.body, "bear") where log.attributes["http.path"] == "/animal"`},
175205
},
176206
},
207+
ProfileStatements: []common.ContextStatements{
208+
{
209+
Context: "profile",
210+
Statements: []string{`set(profile.original_payload_format, "bear") where profile.original_payload_format == "/animal"`},
211+
},
212+
},
177213
},
178214
},
179215
{
@@ -204,6 +240,14 @@ func TestLoadConfig(t *testing.T) {
204240
},
205241
},
206242
},
243+
ProfileStatements: []common.ContextStatements{
244+
{
245+
Statements: []string{
246+
`set(profile.original_payload_format, "bear") where profile.original_payload_format == "/animal"`,
247+
`set(resource.attributes["name"], "bear")`,
248+
},
249+
},
250+
},
207251
},
208252
},
209253
{
@@ -234,6 +278,14 @@ func TestLoadConfig(t *testing.T) {
234278
},
235279
},
236280
},
281+
ProfileStatements: []common.ContextStatements{
282+
{
283+
Statements: []string{
284+
`set(profile.original_payload_format, "bear") where profile.original_payload_format == "/animal"`,
285+
`set(resource.attributes["name"], "bear")`,
286+
},
287+
},
288+
},
237289
},
238290
},
239291
{
@@ -270,6 +322,16 @@ func TestLoadConfig(t *testing.T) {
270322
ErrorMode: "",
271323
},
272324
},
325+
ProfileStatements: []common.ContextStatements{
326+
{
327+
Statements: []string{`set(resource.attributes["name"], "propagate")`},
328+
ErrorMode: ottl.PropagateError,
329+
},
330+
{
331+
Statements: []string{`set(resource.attributes["name"], "ignore")`},
332+
ErrorMode: "",
333+
},
334+
},
273335
},
274336
},
275337
}

processor/transformprocessor/factory.go

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,25 @@ import (
99

1010
"go.opentelemetry.io/collector/component"
1111
"go.opentelemetry.io/collector/consumer"
12+
"go.opentelemetry.io/collector/consumer/xconsumer"
1213
"go.opentelemetry.io/collector/processor"
1314
"go.opentelemetry.io/collector/processor/processorhelper"
15+
"go.opentelemetry.io/collector/processor/processorhelper/xprocessorhelper"
16+
"go.opentelemetry.io/collector/processor/xprocessor"
1417
"go.uber.org/zap"
1518

1619
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
1720
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottldatapoint"
1821
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottllog"
1922
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric"
23+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlprofile"
2024
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan"
2125
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspanevent"
2226
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor/internal/common"
2327
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor/internal/logs"
2428
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor/internal/metadata"
2529
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor/internal/metrics"
30+
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor/internal/profiles"
2631
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor/internal/traces"
2732
)
2833

@@ -34,11 +39,13 @@ type transformProcessorFactory struct {
3439
metricFunctions map[string]ottl.Factory[ottlmetric.TransformContext]
3540
spanEventFunctions map[string]ottl.Factory[ottlspanevent.TransformContext]
3641
spanFunctions map[string]ottl.Factory[ottlspan.TransformContext]
42+
profileFunctions map[string]ottl.Factory[ottlprofile.TransformContext]
3743
defaultDataPointFunctionsOverridden bool
3844
defaultLogFunctionsOverridden bool
3945
defaultMetricFunctionsOverridden bool
4046
defaultSpanEventFunctionsOverridden bool
4147
defaultSpanFunctionsOverridden bool
48+
defaultProfileFunctionsOverridden bool
4249
}
4350

4451
// FactoryOption applies changes to transformProcessorFactory.
@@ -104,6 +111,18 @@ func WithSpanFunctions(spanFunctions []ottl.Factory[ottlspan.TransformContext])
104111
}
105112
}
106113

114+
// WithProfileFunctions will override the default OTTL profile context functions with the provided profileFunctions in the resulting processor.
115+
// Subsequent uses of WithProfileFunctions will merge the provided profileFunctions with the previously registered functions.
116+
func WithProfileFunctions(profileFunctions []ottl.Factory[ottlprofile.TransformContext]) FactoryOption {
117+
return func(factory *transformProcessorFactory) {
118+
if !factory.defaultProfileFunctionsOverridden {
119+
factory.profileFunctions = map[string]ottl.Factory[ottlprofile.TransformContext]{}
120+
factory.defaultProfileFunctionsOverridden = true
121+
}
122+
factory.profileFunctions = mergeFunctionsToMap(factory.profileFunctions, profileFunctions)
123+
}
124+
}
125+
107126
func NewFactory() processor.Factory {
108127
return NewFactoryWithOptions()
109128
}
@@ -116,17 +135,19 @@ func NewFactoryWithOptions(options ...FactoryOption) processor.Factory {
116135
metricFunctions: defaultMetricFunctionsMap(),
117136
spanEventFunctions: defaultSpanEventFunctionsMap(),
118137
spanFunctions: defaultSpanFunctionsMap(),
138+
profileFunctions: defaultProfileFunctionsMap(),
119139
}
120140
for _, o := range options {
121141
o(f)
122142
}
123143

124-
return processor.NewFactory(
144+
return xprocessor.NewFactory(
125145
metadata.Type,
126146
f.createDefaultConfig,
127-
processor.WithLogs(f.createLogsProcessor, metadata.LogsStability),
128-
processor.WithTraces(f.createTracesProcessor, metadata.TracesStability),
129-
processor.WithMetrics(f.createMetricsProcessor, metadata.MetricsStability),
147+
xprocessor.WithLogs(f.createLogsProcessor, metadata.LogsStability),
148+
xprocessor.WithTraces(f.createTracesProcessor, metadata.TracesStability),
149+
xprocessor.WithMetrics(f.createMetricsProcessor, metadata.MetricsStability),
150+
xprocessor.WithProfiles(f.createProfilesProcessor, metadata.ProfilesStability),
130151
)
131152
}
132153

@@ -136,11 +157,13 @@ func (f *transformProcessorFactory) createDefaultConfig() component.Config {
136157
TraceStatements: []common.ContextStatements{},
137158
MetricStatements: []common.ContextStatements{},
138159
LogStatements: []common.ContextStatements{},
160+
ProfileStatements: []common.ContextStatements{},
139161
dataPointFunctions: f.dataPointFunctions,
140162
logFunctions: f.logFunctions,
141163
metricFunctions: f.metricFunctions,
142164
spanEventFunctions: f.spanEventFunctions,
143165
spanFunctions: f.spanFunctions,
166+
profileFunctions: f.profileFunctions,
144167
}
145168
}
146169

@@ -219,3 +242,28 @@ func (f *transformProcessorFactory) createMetricsProcessor(
219242
proc.ProcessMetrics,
220243
processorhelper.WithCapabilities(processorCapabilities))
221244
}
245+
246+
func (f *transformProcessorFactory) createProfilesProcessor(
247+
ctx context.Context,
248+
set processor.Settings,
249+
cfg component.Config,
250+
nextConsumer xconsumer.Profiles,
251+
) (xprocessor.Profiles, error) {
252+
oCfg := cfg.(*Config)
253+
oCfg.logger = set.Logger
254+
255+
if f.defaultProfileFunctionsOverridden {
256+
set.Logger.Debug("non-default OTTL profile functions have been registered in the \"transform\" processor", zap.Bool("profile", f.defaultProfileFunctionsOverridden))
257+
}
258+
proc, err := profiles.NewProcessor(oCfg.ProfileStatements, oCfg.ErrorMode, set.TelemetrySettings, f.profileFunctions)
259+
if err != nil {
260+
return nil, fmt.Errorf("invalid config for \"transform\" processor %w", err)
261+
}
262+
return xprocessorhelper.NewProfiles(
263+
ctx,
264+
set,
265+
cfg,
266+
nextConsumer,
267+
proc.ProcessProfiles,
268+
xprocessorhelper.WithCapabilities(processorCapabilities))
269+
}

0 commit comments

Comments
 (0)