Skip to content

Commit 56a93c7

Browse files
authored
[connector/elasticapm]Add support for adding custom attrs to aggregated metrics (#698)
1 parent d336d78 commit 56a93c7

File tree

43 files changed

+3813
-155
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+3813
-155
lines changed

connector/elasticapmconnector/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,17 @@ in `elasticapm::aggregation::metadata_keys`.
4141

4242
By default, cardinality for aggregated metrics will be limited.
4343
Each limit defines a `max_cardinality`. There are four limits that can be configured:
44-
- `elasticapm::aggregation::limit::resource`: configures the max cardinality of resources
45-
- `elasticapm::aggregation::limit::scope`: configures the max cardinality of scopes within a resource
46-
- `elasticapm::aggregation::limit::metric`: configures the max cardinality of metrics within a scope
47-
- `elasticapm::aggregation::limit::datapoint`: configures the max cardinality of datapoints within a metric
44+
- `elasticapm::aggregation::limits::resource`: configures the max cardinality of resources
45+
- `elasticapm::aggregation::limits::scope`: configures the max cardinality of scopes within a resource
46+
- `elasticapm::aggregation::limits::metric`: configures the max cardinality of metrics within a scope
47+
- `elasticapm::aggregation::limits::datapoint`: configures the max cardinality of datapoints within a metric
4848

4949
```yaml
5050
elasticapm:
5151
aggregation:
5252
directory: /path/to/aggregation/directory
5353
metadata_keys: [list, of, metadata, keys]
54-
limit:
54+
limits:
5555
resource:
5656
max_cardinality: 8000
5757
scope:

connector/elasticapmconnector/config.go

Lines changed: 113 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package elasticapmconnector // import "github.com/elastic/opentelemetry-collecto
1919

2020
import (
2121
"fmt"
22+
"slices"
2223
"time"
2324

2425
signaltometricsconfig "github.com/open-telemetry/opentelemetry-collector-contrib/connector/signaltometricsconnector/config"
@@ -39,6 +40,25 @@ type Config struct {
3940
// Aggregation holds configuration related to aggregation of Elastic APM
4041
// metrics from other signals.
4142
Aggregation *AggregationConfig `mapstructure:"aggregation"`
43+
44+
// CustomResourceAttributes define a list of resource attributes that will
45+
// be added to all the aggregated metrics as optional attributes i.e. the
46+
// attribute will be added to the aggregated metrics if they are present in
47+
// the incoming signal, otherwise, the attribute will be ignored.
48+
//
49+
// NOTE: any custom attributes should have a bounded and preferably low
50+
// cardinality to be performant.
51+
CustomResourceAttributes []string `mapstructure:"custom_resource_attributes"`
52+
53+
// CustomSpanAttributes define a list of span attributes that will be added
54+
// to the aggregated `service_transaction`, `transaction`, and `span_destination`
55+
// metrics as optional attributes i.e. the attribute will be added to the
56+
// aggregated metrics if they are present in the incoming signal, otherwise,
57+
// the attribute will be ignored.
58+
//
59+
// NOTE: any custom attributes should have a bounded and preferably low
60+
// cardinality to be performant.
61+
CustomSpanAttributes []string `mapstructure:"custom_span_attributes"`
4262
}
4363

4464
type AggregationConfig struct {
@@ -66,8 +86,8 @@ type AggregationConfig struct {
6686
// and will not be supported.
6787
Intervals []time.Duration `mapstructure:"intervals"`
6888

69-
// Limit holds optional cardinality limits for aggregated metrics
70-
Limit AggregationLimitConfig `mapstructure:"limit"`
89+
// Limits holds optional cardinality limits for aggregated metrics
90+
Limits AggregationLimitConfig `mapstructure:"limits"`
7191
}
7292

7393
type AggregationLimitConfig struct {
@@ -119,7 +139,7 @@ func (cfg Config) lsmConfig() *lsmconfig.Config {
119139
lsmConfig.Directory = cfg.Aggregation.Directory
120140
lsmConfig.MetadataKeys = cfg.Aggregation.MetadataKeys
121141
lsmConfig.ResourceLimit = lsmconfig.LimitConfig{
122-
MaxCardinality: cfg.Aggregation.Limit.ResourceLimit.MaxCardinality,
142+
MaxCardinality: cfg.Aggregation.Limits.ResourceLimit.MaxCardinality,
123143
Overflow: lsmconfig.OverflowConfig{
124144
Attributes: []lsmconfig.Attribute{
125145
{Key: "service.name", Value: "_other"}, // Specific attribute required for APU UI compatibility
@@ -128,23 +148,23 @@ func (cfg Config) lsmConfig() *lsmconfig.Config {
128148
},
129149
}
130150
lsmConfig.ScopeLimit = lsmconfig.LimitConfig{
131-
MaxCardinality: cfg.Aggregation.Limit.ScopeLimit.MaxCardinality,
151+
MaxCardinality: cfg.Aggregation.Limits.ScopeLimit.MaxCardinality,
132152
Overflow: lsmconfig.OverflowConfig{
133153
Attributes: []lsmconfig.Attribute{
134154
{Key: "overflow", Value: "scope"},
135155
},
136156
},
137157
}
138158
lsmConfig.MetricLimit = lsmconfig.LimitConfig{
139-
MaxCardinality: cfg.Aggregation.Limit.MetricLimit.MaxCardinality,
159+
MaxCardinality: cfg.Aggregation.Limits.MetricLimit.MaxCardinality,
140160
Overflow: lsmconfig.OverflowConfig{
141161
Attributes: []lsmconfig.Attribute{
142162
{Key: "overflow", Value: "metric"},
143163
},
144164
},
145165
}
146166
lsmConfig.DatapointLimit = lsmconfig.LimitConfig{
147-
MaxCardinality: cfg.Aggregation.Limit.DatapointLimit.MaxCardinality,
167+
MaxCardinality: cfg.Aggregation.Limits.DatapointLimit.MaxCardinality,
148168
Overflow: lsmconfig.OverflowConfig{
149169
Attributes: []lsmconfig.Attribute{
150170
{Key: "overflow", Value: "datapoint"},
@@ -156,72 +176,88 @@ func (cfg Config) lsmConfig() *lsmconfig.Config {
156176
}
157177

158178
func (cfg Config) signaltometricsConfig() *signaltometricsconfig.Config {
159-
// serviceResourceAttributes is the resource attributes included in
160-
// service-level aggregated metrics.
161-
serviceResourceAttributes := []signaltometricsconfig.Attribute{
162-
{Key: "service.name"},
163-
{Key: "deployment.environment"}, // service.environment
164-
{Key: "telemetry.sdk.language"}, // service.language.name
165-
166-
// agent.name is set via elastictraceprocessor for traces,
167-
// but not for other signals. Default to "unknown" for the
168-
// others.
169-
{
170-
Key: "agent.name",
171-
DefaultValue: "unknown",
172-
},
173-
}
179+
// commonResourceAttributes are resource attributes included in
180+
// all aggregated metrics.
181+
commonResourceAttributes := append(
182+
[]signaltometricsconfig.Attribute{
183+
{Key: "service.name"},
184+
{Key: "deployment.environment"}, // service.environment
185+
{Key: "telemetry.sdk.language"}, // service.language.name
186+
187+
// agent.name is set via elastictraceprocessor for traces,
188+
// but not for other signals. Default to "unknown" for the
189+
// others.
190+
{
191+
Key: "agent.name",
192+
DefaultValue: "unknown",
193+
},
194+
}, toSignalToMetricsAttributes(cfg.CustomResourceAttributes)...,
195+
)
196+
197+
// serviceSummaryResourceAttributes are resource attributes for service
198+
// summary metrics.
199+
serviceSummaryResourceAttributes := slices.Clone(commonResourceAttributes)
200+
201+
// serviceTransactionResourceAttributes are resource attributes for service
202+
// transaction metrics
203+
serviceTransactionResourceAttributes := slices.Clone(commonResourceAttributes)
174204

175-
// transactionResourceAttributes is the resource attributes included
205+
// transactionResourceAttributes are resource attributes included
176206
// in transaction group-level aggregated metrics.
177-
transactionResourceAttributes := append([]signaltometricsconfig.Attribute{
178-
{Key: "container.id"},
179-
{Key: "k8s.pod.name"},
180-
{Key: "service.version"},
181-
{Key: "service.instance.id"}, // service.node.name
182-
{Key: "process.runtime.name"}, // service.runtime.name
183-
{Key: "process.runtime.version"}, // service.runtime.version
184-
{Key: "telemetry.sdk.version"}, // service.language.version??
185-
{Key: "host.name"},
186-
{Key: "os.type"}, // host.os.platform
187-
{Key: "faas.instance"},
188-
{Key: "faas.name"},
189-
{Key: "faas.version"},
190-
{Key: "cloud.provider"},
191-
{Key: "cloud.region"},
192-
{Key: "cloud.availability_zone"},
193-
{Key: "cloud.platform"}, // cloud.service.name
194-
{Key: "cloud.account.id"},
195-
}, serviceResourceAttributes...)
207+
transactionResourceAttributes := append(
208+
[]signaltometricsconfig.Attribute{
209+
{Key: "container.id"},
210+
{Key: "k8s.pod.name"},
211+
{Key: "service.version"},
212+
{Key: "service.instance.id"}, // service.node.name
213+
{Key: "process.runtime.name"}, // service.runtime.name
214+
{Key: "process.runtime.version"}, // service.runtime.version
215+
{Key: "telemetry.sdk.version"}, // service.language.version??
216+
{Key: "host.name"},
217+
{Key: "os.type"}, // host.os.platform
218+
{Key: "faas.instance"},
219+
{Key: "faas.name"},
220+
{Key: "faas.version"},
221+
{Key: "cloud.provider"},
222+
{Key: "cloud.region"},
223+
{Key: "cloud.availability_zone"},
224+
{Key: "cloud.platform"}, // cloud.service.name
225+
{Key: "cloud.account.id"},
226+
}, commonResourceAttributes...,
227+
)
228+
229+
// spanDestinationResourceAttributes are resource attributes included
230+
// in service destination aggregations
231+
spanDestinationResourceAttributes := slices.Clone(commonResourceAttributes)
196232

197233
serviceSummaryAttributes := []signaltometricsconfig.Attribute{{
198234
Key: "metricset.name",
199235
DefaultValue: "service_summary",
200236
}}
201237

202-
serviceTransactionAttributes := []signaltometricsconfig.Attribute{
238+
serviceTransactionAttributes := append([]signaltometricsconfig.Attribute{
203239
{Key: "transaction.root"},
204240
{Key: "transaction.type"},
205241
{Key: "metricset.name", DefaultValue: "service_transaction"},
206-
}
242+
}, toSignalToMetricsAttributes(cfg.CustomSpanAttributes)...)
207243

208-
transactionAttributes := []signaltometricsconfig.Attribute{
244+
transactionAttributes := append([]signaltometricsconfig.Attribute{
209245
{Key: "transaction.root"},
210246
{Key: "transaction.name"},
211247
{Key: "transaction.type"},
212248
{Key: "transaction.result"},
213249
{Key: "event.outcome"},
214250
{Key: "metricset.name", DefaultValue: "transaction"},
215-
}
251+
}, toSignalToMetricsAttributes(cfg.CustomSpanAttributes)...)
216252

217-
serviceDestinationAttributes := []signaltometricsconfig.Attribute{
253+
spanDestinationAttributes := append([]signaltometricsconfig.Attribute{
218254
{Key: "span.name"},
219255
{Key: "event.outcome"},
220256
{Key: "service.target.type"},
221257
{Key: "service.target.name"},
222258
{Key: "span.destination.service.resource"},
223259
{Key: "metricset.name", DefaultValue: "service_destination"},
224-
}
260+
}, toSignalToMetricsAttributes(cfg.CustomSpanAttributes)...)
225261

226262
transactionDurationHistogram := &signaltometricsconfig.ExponentialHistogram{
227263
Count: "Int(AdjustedCount())",
@@ -237,30 +273,30 @@ func (cfg Config) signaltometricsConfig() *signaltometricsconfig.Config {
237273
return &signaltometricsconfig.Config{
238274
Logs: []signaltometricsconfig.MetricInfo{{
239275
Name: "service_summary",
240-
IncludeResourceAttributes: serviceResourceAttributes,
276+
IncludeResourceAttributes: serviceSummaryResourceAttributes,
241277
Attributes: serviceSummaryAttributes,
242278
Sum: &signaltometricsconfig.Sum{Value: "1"},
243279
}},
244280

245281
Datapoints: []signaltometricsconfig.MetricInfo{{
246282
Name: "service_summary",
247-
IncludeResourceAttributes: serviceResourceAttributes,
283+
IncludeResourceAttributes: serviceSummaryResourceAttributes,
248284
Attributes: serviceSummaryAttributes,
249285
Sum: &signaltometricsconfig.Sum{Value: "1"},
250286
}},
251287

252288
Spans: []signaltometricsconfig.MetricInfo{{
253289
Name: "service_summary",
254-
IncludeResourceAttributes: serviceResourceAttributes,
290+
IncludeResourceAttributes: serviceSummaryResourceAttributes,
255291
Attributes: serviceSummaryAttributes,
256292
Sum: &signaltometricsconfig.Sum{
257293
Value: "Int(AdjustedCount())",
258294
},
259295
}, {
260296
Name: "transaction.duration.histogram",
261297
Description: "APM service transaction aggregated metrics as histogram",
262-
IncludeResourceAttributes: serviceResourceAttributes,
263-
Attributes: append(serviceTransactionAttributes[:], signaltometricsconfig.Attribute{
298+
IncludeResourceAttributes: serviceTransactionResourceAttributes,
299+
Attributes: append(slices.Clone(serviceTransactionAttributes), signaltometricsconfig.Attribute{
264300
Key: "elasticsearch.mapping.hints",
265301
DefaultValue: []any{"_doc_count"},
266302
}),
@@ -269,8 +305,8 @@ func (cfg Config) signaltometricsConfig() *signaltometricsconfig.Config {
269305
}, {
270306
Name: "transaction.duration.summary",
271307
Description: "APM service transaction aggregated metrics as summary",
272-
IncludeResourceAttributes: serviceResourceAttributes,
273-
Attributes: append(serviceTransactionAttributes[:], signaltometricsconfig.Attribute{
308+
IncludeResourceAttributes: serviceTransactionResourceAttributes,
309+
Attributes: append(slices.Clone(serviceTransactionAttributes), signaltometricsconfig.Attribute{
274310
Key: "elasticsearch.mapping.hints",
275311
DefaultValue: []any{"aggregate_metric_double"},
276312
}),
@@ -280,7 +316,7 @@ func (cfg Config) signaltometricsConfig() *signaltometricsconfig.Config {
280316
Name: "transaction.duration.histogram",
281317
Description: "APM transaction aggregated metrics as histogram",
282318
IncludeResourceAttributes: transactionResourceAttributes,
283-
Attributes: append(transactionAttributes[:], signaltometricsconfig.Attribute{
319+
Attributes: append(slices.Clone(transactionAttributes), signaltometricsconfig.Attribute{
284320
Key: "elasticsearch.mapping.hints",
285321
DefaultValue: []any{"_doc_count"},
286322
}),
@@ -290,7 +326,7 @@ func (cfg Config) signaltometricsConfig() *signaltometricsconfig.Config {
290326
Name: "transaction.duration.summary",
291327
Description: "APM transaction aggregated metrics as summary",
292328
IncludeResourceAttributes: transactionResourceAttributes,
293-
Attributes: append(transactionAttributes[:], signaltometricsconfig.Attribute{
329+
Attributes: append(slices.Clone(transactionAttributes), signaltometricsconfig.Attribute{
294330
Key: "elasticsearch.mapping.hints",
295331
DefaultValue: []any{"aggregate_metric_double"},
296332
}),
@@ -299,17 +335,17 @@ func (cfg Config) signaltometricsConfig() *signaltometricsconfig.Config {
299335
}, {
300336
Name: "span.destination.service.response_time.sum.us",
301337
Description: "APM span destination metrics",
302-
IncludeResourceAttributes: serviceResourceAttributes,
303-
Attributes: serviceDestinationAttributes,
338+
IncludeResourceAttributes: spanDestinationResourceAttributes,
339+
Attributes: spanDestinationAttributes,
304340
Unit: "us",
305341
Sum: &signaltometricsconfig.Sum{
306342
Value: "Double(Microseconds(end_time - start_time))",
307343
},
308344
}, {
309345
Name: "span.destination.service.response_time.count",
310346
Description: "APM span destination metrics",
311-
IncludeResourceAttributes: serviceResourceAttributes,
312-
Attributes: serviceDestinationAttributes,
347+
IncludeResourceAttributes: spanDestinationResourceAttributes,
348+
Attributes: spanDestinationAttributes,
313349
Sum: &signaltometricsconfig.Sum{
314350
Value: "Int(AdjustedCount())",
315351
},
@@ -321,8 +357,8 @@ func (cfg Config) signaltometricsConfig() *signaltometricsconfig.Config {
321357
// values are required and the actual histogram bucket is ignored.
322358
Name: "event.success_count",
323359
Description: "Success count as a metric for service transaction",
324-
IncludeResourceAttributes: serviceResourceAttributes,
325-
Attributes: append(serviceTransactionAttributes[:], signaltometricsconfig.Attribute{
360+
IncludeResourceAttributes: serviceTransactionResourceAttributes,
361+
Attributes: append(slices.Clone(serviceTransactionAttributes), signaltometricsconfig.Attribute{
326362
Key: "elasticsearch.mapping.hints",
327363
DefaultValue: []any{"aggregate_metric_double"},
328364
}),
@@ -338,8 +374,8 @@ func (cfg Config) signaltometricsConfig() *signaltometricsconfig.Config {
338374
}, {
339375
Name: "event.success_count",
340376
Description: "Success count as a metric for service transaction",
341-
IncludeResourceAttributes: serviceResourceAttributes,
342-
Attributes: append(serviceTransactionAttributes[:], signaltometricsconfig.Attribute{
377+
IncludeResourceAttributes: serviceTransactionResourceAttributes,
378+
Attributes: append(slices.Clone(serviceTransactionAttributes), signaltometricsconfig.Attribute{
343379
Key: "elasticsearch.mapping.hints",
344380
DefaultValue: []any{"aggregate_metric_double"},
345381
}),
@@ -355,3 +391,16 @@ func (cfg Config) signaltometricsConfig() *signaltometricsconfig.Config {
355391
}},
356392
}
357393
}
394+
395+
// toSignalToMetricsAttributes converts slice to string to signal to metricsa attributes
396+
// assuming `optional: true` for each attribute.
397+
func toSignalToMetricsAttributes(in []string) []signaltometricsconfig.Attribute {
398+
attrs := make([]signaltometricsconfig.Attribute, 0, len(in))
399+
for _, k := range in {
400+
attrs = append(attrs, signaltometricsconfig.Attribute{
401+
Key: k,
402+
Optional: true,
403+
})
404+
}
405+
return attrs
406+
}

0 commit comments

Comments
 (0)