Skip to content

Commit cc63942

Browse files
authored
Add profiling support to the count connector (#39577)
1 parent 588b225 commit cc63942

20 files changed

+1404
-13
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: countconnector
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
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: [39577]
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]

connector/countconnector/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
| traces | metrics | [alpha] |
1919
| metrics | metrics | [alpha] |
2020
| logs | metrics | [alpha] |
21+
| profiles | metrics | [alpha] |
2122

2223
[Exporter Pipeline Type]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/connector/README.md#exporter-pipeline-type
2324
[Receiver Pipeline Type]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/connector/README.md#receiver-pipeline-type

connector/countconnector/config.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ const (
2929

3030
defaultMetricNameLogs = "log.record.count"
3131
defaultMetricDescLogs = "The number of log records observed."
32+
33+
defaultMetricNameProfiles = "profile.count"
34+
defaultMetricDescProfiles = "The number of profiles observed."
3235
)
3336

3437
// Config for the connector
@@ -38,6 +41,7 @@ type Config struct {
3841
Metrics map[string]MetricInfo `mapstructure:"metrics"`
3942
DataPoints map[string]MetricInfo `mapstructure:"datapoints"`
4043
Logs map[string]MetricInfo `mapstructure:"logs"`
44+
Profiles map[string]MetricInfo `mapstructure:"profiles"`
4145
// prevent unkeyed literal initialization
4246
_ struct{}
4347
}
@@ -115,6 +119,17 @@ func (c *Config) Validate() error {
115119
return fmt.Errorf("logs attributes: metric %q: %w", name, err)
116120
}
117121
}
122+
for name, info := range c.Profiles {
123+
if name == "" {
124+
return errors.New("profiles: metric name missing")
125+
}
126+
if _, err := filterottl.NewBoolExprForProfile(info.Conditions, filterottl.StandardProfileFuncs(), ottl.PropagateError, component.TelemetrySettings{Logger: zap.NewNop()}); err != nil {
127+
return fmt.Errorf("profiles condition: metric %q: %w", name, err)
128+
}
129+
if err := info.validateAttributes(); err != nil {
130+
return fmt.Errorf("profiles attributes: metric %q: %w", name, err)
131+
}
132+
}
118133
return nil
119134
}
120135

@@ -155,6 +170,9 @@ func (c *Config) Unmarshal(componentParser *confmap.Conf) error {
155170
if !componentParser.IsSet("logs") {
156171
c.Logs = defaultLogsConfig()
157172
}
173+
if !componentParser.IsSet("profiles") {
174+
c.Profiles = defaultProfilesConfig()
175+
}
158176
return nil
159177
}
160178

@@ -197,3 +215,11 @@ func defaultLogsConfig() map[string]MetricInfo {
197215
},
198216
}
199217
}
218+
219+
func defaultProfilesConfig() map[string]MetricInfo {
220+
return map[string]MetricInfo{
221+
defaultMetricNameProfiles: {
222+
Description: defaultMetricDescProfiles,
223+
},
224+
}
225+
}

connector/countconnector/config_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ func TestLoadConfig(t *testing.T) {
4949
Description: defaultMetricDescLogs,
5050
},
5151
},
52+
Profiles: map[string]MetricInfo{
53+
defaultMetricNameProfiles: {
54+
Description: defaultMetricDescProfiles,
55+
},
56+
},
5257
},
5358
},
5459
{
@@ -79,6 +84,11 @@ func TestLoadConfig(t *testing.T) {
7984
Description: "My description for default log count metric.",
8085
},
8186
},
87+
Profiles: map[string]MetricInfo{
88+
defaultMetricNameProfiles: {
89+
Description: "My description for default profile count metric.",
90+
},
91+
},
8292
},
8393
},
8494
{
@@ -109,6 +119,11 @@ func TestLoadConfig(t *testing.T) {
109119
Description: "My log record count.",
110120
},
111121
},
122+
Profiles: map[string]MetricInfo{
123+
"my.profiling.count": {
124+
Description: "My profile count.",
125+
},
126+
},
112127
},
113128
},
114129
{
@@ -144,6 +159,12 @@ func TestLoadConfig(t *testing.T) {
144159
Conditions: []string{`IsMatch(resource.attributes["host.name"], "pod-l")`},
145160
},
146161
},
162+
Profiles: map[string]MetricInfo{
163+
"my.profiling.count": {
164+
Description: "My profile count.",
165+
Conditions: []string{`IsMatch(resource.attributes["host.name"], "pod-l")`},
166+
},
167+
},
147168
},
148169
},
149170
{
@@ -194,6 +215,15 @@ func TestLoadConfig(t *testing.T) {
194215
},
195216
},
196217
},
218+
Profiles: map[string]MetricInfo{
219+
"my.profiling.count": {
220+
Description: "My profile count.",
221+
Conditions: []string{
222+
`IsMatch(resource.attributes["host.name"], "pod-l")`,
223+
`IsMatch(resource.attributes["foo"], "bar-l")`,
224+
},
225+
},
226+
},
197227
},
198228
},
199229
{
@@ -236,6 +266,14 @@ func TestLoadConfig(t *testing.T) {
236266
},
237267
},
238268
},
269+
Profiles: map[string]MetricInfo{
270+
"my.profiling.count": {
271+
Description: "My profile count by environment.",
272+
Attributes: []AttributeConfig{
273+
{Key: "env"},
274+
},
275+
},
276+
},
239277
},
240278
},
241279
{
@@ -322,6 +360,24 @@ func TestLoadConfig(t *testing.T) {
322360
},
323361
},
324362
},
363+
Profiles: map[string]MetricInfo{
364+
"my.profiling.count": {
365+
Description: "My profile count.",
366+
},
367+
"limited.profiling.count": {
368+
Description: "Limited profile count.",
369+
Conditions: []string{`IsMatch(resource.attributes["host.name"], "pod-l")`},
370+
Attributes: []AttributeConfig{
371+
{
372+
Key: "env",
373+
},
374+
{
375+
Key: "component",
376+
DefaultValue: "other",
377+
},
378+
},
379+
},
380+
},
325381
},
326382
},
327383
{
@@ -369,6 +425,11 @@ func TestLoadConfig(t *testing.T) {
369425
},
370426
},
371427
},
428+
Profiles: map[string]MetricInfo{
429+
defaultMetricNameProfiles: {
430+
Description: defaultMetricDescProfiles,
431+
},
432+
},
372433
},
373434
},
374435
}
@@ -451,6 +512,17 @@ func TestConfigErrors(t *testing.T) {
451512
},
452513
expect: "logs: metric name missing",
453514
},
515+
{
516+
name: "missing_metric_name_profile",
517+
input: &Config{
518+
Profiles: map[string]MetricInfo{
519+
"": {
520+
Description: defaultMetricDescSpans,
521+
},
522+
},
523+
},
524+
expect: "profiles: metric name missing",
525+
},
454526
{
455527
name: "invalid_condition_span",
456528
input: &Config{
@@ -511,6 +583,18 @@ func TestConfigErrors(t *testing.T) {
511583
},
512584
expect: fmt.Sprintf("logs condition: metric %q: unable to parse OTTL condition", defaultMetricNameLogs),
513585
},
586+
{
587+
name: "invalid_condition_profile",
588+
input: &Config{
589+
Profiles: map[string]MetricInfo{
590+
defaultMetricNameProfiles: {
591+
Description: defaultMetricDescSpans,
592+
Conditions: []string{"invalid condition"},
593+
},
594+
},
595+
},
596+
expect: fmt.Sprintf("profiles condition: metric %q: unable to parse OTTL condition", defaultMetricNameProfiles),
597+
},
514598
}
515599

516600
for _, tc := range testCases {

connector/countconnector/connector.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,20 @@ import (
1313
"go.opentelemetry.io/collector/pdata/pcommon"
1414
"go.opentelemetry.io/collector/pdata/plog"
1515
"go.opentelemetry.io/collector/pdata/pmetric"
16+
"go.opentelemetry.io/collector/pdata/pprofile"
1617
"go.opentelemetry.io/collector/pdata/ptrace"
1718

1819
"github.com/open-telemetry/opentelemetry-collector-contrib/connector/countconnector/internal/metadata"
1920
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottldatapoint"
2021
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottllog"
2122
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric"
23+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlprofile"
2224
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan"
2325
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspanevent"
2426
)
2527

26-
// count can count spans, span event, metrics, data points, or log records
27-
// and emit the counts onto a metrics pipeline.
28+
// count can count spans, span event, metrics, data points, log records or
29+
// profiles and emit the counts onto a metrics pipeline.
2830
type count struct {
2931
metricsConsumer consumer.Metrics
3032
component.StartFunc
@@ -35,6 +37,7 @@ type count struct {
3537
metricsMetricDefs map[string]metricDef[ottlmetric.TransformContext]
3638
dataPointsMetricDefs map[string]metricDef[ottldatapoint.TransformContext]
3739
logsMetricDefs map[string]metricDef[ottllog.TransformContext]
40+
profilesMetricDefs map[string]metricDef[ottlprofile.TransformContext]
3841
}
3942

4043
func (c *count) Capabilities() consumer.Capabilities {
@@ -198,3 +201,42 @@ func (c *count) ConsumeLogs(ctx context.Context, ld plog.Logs) error {
198201
}
199202
return c.metricsConsumer.ConsumeMetrics(ctx, countMetrics)
200203
}
204+
205+
func (c *count) ConsumeProfiles(ctx context.Context, ld pprofile.Profiles) error {
206+
var multiError error
207+
countMetrics := pmetric.NewMetrics()
208+
countMetrics.ResourceMetrics().EnsureCapacity(ld.ResourceProfiles().Len())
209+
for i := 0; i < ld.ResourceProfiles().Len(); i++ {
210+
resourceProfile := ld.ResourceProfiles().At(i)
211+
counter := newCounter[ottlprofile.TransformContext](c.profilesMetricDefs)
212+
213+
for j := 0; j < resourceProfile.ScopeProfiles().Len(); j++ {
214+
scopeProfile := resourceProfile.ScopeProfiles().At(j)
215+
216+
for k := 0; k < scopeProfile.Profiles().Len(); k++ {
217+
profile := scopeProfile.Profiles().At(k)
218+
219+
pCtx := ottlprofile.NewTransformContext(profile, scopeProfile.Scope(), resourceProfile.Resource(), scopeProfile, resourceProfile)
220+
attributes := pprofile.FromAttributeIndices(profile.AttributeTable(), profile)
221+
multiError = errors.Join(multiError, counter.update(ctx, attributes, pCtx))
222+
}
223+
}
224+
225+
if len(counter.counts) == 0 {
226+
continue // don't add an empty resource
227+
}
228+
229+
countResource := countMetrics.ResourceMetrics().AppendEmpty()
230+
resourceProfile.Resource().Attributes().CopyTo(countResource.Resource().Attributes())
231+
232+
countResource.ScopeMetrics().EnsureCapacity(resourceProfile.ScopeProfiles().Len())
233+
countScope := countResource.ScopeMetrics().AppendEmpty()
234+
countScope.Scope().SetName(metadata.ScopeName)
235+
236+
counter.appendMetricsTo(countScope.Metrics())
237+
}
238+
if multiError != nil {
239+
return multiError
240+
}
241+
return c.metricsConsumer.ConsumeMetrics(ctx, countMetrics)
242+
}

0 commit comments

Comments
 (0)