diff --git a/.chloggen/10726-reag.yaml b/.chloggen/10726-reag.yaml new file mode 100644 index 00000000000..bed1e976f1d --- /dev/null +++ b/.chloggen/10726-reag.yaml @@ -0,0 +1,25 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: 'enhancement' + +# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver) +component: mdatagen + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: "Add support for metric re-aggregation by disabling attributes in metadata.yaml and user config" + +# One or more tracking issues or pull requests related to the change +issues: [10726] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/cmd/mdatagen/internal/loader_test.go b/cmd/mdatagen/internal/loader_test.go index 4ea52e111d9..f8fbb7c5f28 100644 --- a/cmd/mdatagen/internal/loader_test.go +++ b/cmd/mdatagen/internal/loader_test.go @@ -153,6 +153,7 @@ func TestLoadMetadata(t *testing.T) { ValueType: pcommon.ValueTypeStr, }, FullName: "enum_attr", + Enabled: true, }, "string_attr": { Description: "Attribute with any string value.", @@ -161,6 +162,7 @@ func TestLoadMetadata(t *testing.T) { ValueType: pcommon.ValueTypeStr, }, FullName: "string_attr", + Enabled: false, }, "overridden_int_attr": { Description: "Integer attribute with overridden name.", @@ -169,6 +171,7 @@ func TestLoadMetadata(t *testing.T) { ValueType: pcommon.ValueTypeInt, }, FullName: "overridden_int_attr", + Enabled: true, }, "boolean_attr": { Description: "Attribute with a boolean value.", @@ -176,6 +179,7 @@ func TestLoadMetadata(t *testing.T) { ValueType: pcommon.ValueTypeBool, }, FullName: "boolean_attr", + Enabled: true, }, "boolean_attr2": { Description: "Another attribute with a boolean value.", @@ -183,6 +187,7 @@ func TestLoadMetadata(t *testing.T) { ValueType: pcommon.ValueTypeBool, }, FullName: "boolean_attr2", + Enabled: true, }, "slice_attr": { Description: "Attribute with a slice value.", @@ -190,6 +195,7 @@ func TestLoadMetadata(t *testing.T) { ValueType: pcommon.ValueTypeSlice, }, FullName: "slice_attr", + Enabled: true, }, "map_attr": { Description: "Attribute with a map value.", @@ -197,6 +203,7 @@ func TestLoadMetadata(t *testing.T) { ValueType: pcommon.ValueTypeMap, }, FullName: "map_attr", + Enabled: true, }, "optional_int_attr": { Description: "An optional attribute with an integer value", @@ -205,6 +212,7 @@ func TestLoadMetadata(t *testing.T) { }, FullName: "optional_int_attr", Optional: true, + Enabled: true, }, "optional_string_attr": { Description: "An optional attribute with any string value", @@ -213,8 +221,10 @@ func TestLoadMetadata(t *testing.T) { }, FullName: "optional_string_attr", Optional: true, + Enabled: true, }, }, + Metrics: map[MetricName]Metric{ "default.metric": { Signal: Signal{ @@ -454,7 +464,7 @@ func TestLoadMetadata(t *testing.T) { { name: "testdata/invalid_type_rattr.yaml", want: Metadata{}, - wantErr: "decoding failed due to the following error(s):\n\n'resource_attributes[string.resource.attr].type' invalid type: \"invalidtype\"", + wantErr: "decoding failed due to the following error(s):\n\n'type' invalid type: \"invalidtype\"", }, { name: "testdata/no_enabled.yaml", @@ -484,7 +494,7 @@ func TestLoadMetadata(t *testing.T) { { name: "testdata/invalid_type_attr.yaml", want: Metadata{}, - wantErr: "decoding failed due to the following error(s):\n\n'attributes[used_attr].type' invalid type: \"invalidtype\"", + wantErr: "decoding failed due to the following error(s):\n\n'type' invalid type: \"invalidtype\"", }, { name: "testdata/~~this file doesn't exist~~.yaml", diff --git a/cmd/mdatagen/internal/metadata.go b/cmd/mdatagen/internal/metadata.go index 3fe2cbe82c9..2f0909b6f20 100644 --- a/cmd/mdatagen/internal/metadata.go +++ b/cmd/mdatagen/internal/metadata.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" + "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/filter" "go.opentelemetry.io/collector/pdata/pcommon" ) @@ -310,6 +311,13 @@ type Attribute struct { Optional bool `mapstructure:"optional"` } +func (a *Attribute) Unmarshal(parser *confmap.Conf) error { + if !parser.IsSet("enabled") { + return errors.New("missing required field for attribute: `enabled`") + } + return parser.Unmarshal(a) +} + // Name returns actual name of the attribute that is set on the metric after applying NameOverride. func (a Attribute) Name() AttributeName { if a.NameOverride != "" { @@ -343,6 +351,31 @@ func (a Attribute) TestValue() string { return "" } +func (a Attribute) TestValueTwo() string { + if len(a.Enum) >= 2 { + return fmt.Sprintf(`%q`, a.Enum[1]) + } + switch a.Type.ValueType { + case pcommon.ValueTypeEmpty: + return "" + case pcommon.ValueTypeStr: + return fmt.Sprintf(`"%s-val-2"`, a.FullName) + case pcommon.ValueTypeInt: + return strconv.Itoa(len(a.FullName) + 1) + case pcommon.ValueTypeDouble: + return fmt.Sprintf("%f", 1.1+float64(len(a.FullName))) + case pcommon.ValueTypeBool: + return strconv.FormatBool(len(a.FullName)%2 == 1) + case pcommon.ValueTypeMap: + return fmt.Sprintf(`map[string]any{"key3": "%s-val3", "key4": "%s-val4"}`, a.FullName, a.FullName) + case pcommon.ValueTypeSlice: + return fmt.Sprintf(`[]any{"%s-item3", "%s-item4"}`, a.FullName, a.FullName) + case pcommon.ValueTypeBytes: + return fmt.Sprintf(`[]byte("%s-val-2")`, a.FullName) + } + return "" +} + type Signal struct { // Enabled defines whether the signal is enabled by default. Enabled bool `mapstructure:"enabled"` diff --git a/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_config.go b/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_config.go index 05ddc47ac40..89dbc5aa70e 100644 --- a/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_config.go +++ b/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_config.go @@ -3,13 +3,16 @@ package metadata import ( + "fmt" + "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/filter" ) // MetricConfig provides common config for a particular metric. type MetricConfig struct { - Enabled bool `mapstructure:"enabled"` + Enabled bool `mapstructure:"enabled"` + AggregationStrategy string `mapstructure:"aggregation_strategy"` enabledSetByUser bool } @@ -22,10 +25,23 @@ func (ms *MetricConfig) Unmarshal(parser *confmap.Conf) error { if err != nil { return err } + + if ms.AggregationStrategy != AggregationStrategySum && + ms.AggregationStrategy != AggregationStrategyAvg && + ms.AggregationStrategy != AggregationStrategyMin && + ms.AggregationStrategy != AggregationStrategyMax { + return fmt.Errorf("invalid aggregation strategy set: '%v'", ms.AggregationStrategy) + } + ms.enabledSetByUser = parser.IsSet("enabled") return nil } +// AttributeConfig holds configuration information for a particular metric. +type AttributeConfig struct { + Enabled bool `mapstructure:"enabled"` +} + // MetricsConfig provides config for sample metrics. type MetricsConfig struct { DefaultMetric MetricConfig `mapstructure:"default.metric"` @@ -35,22 +51,65 @@ type MetricsConfig struct { OptionalMetricEmptyUnit MetricConfig `mapstructure:"optional.metric.empty_unit"` } +// AttributesConfig is the collected configuration for all attributes in the +// component +type AttributesConfig struct { + BooleanAttr AttributeConfig `mapstructure:"boolean_attr"` + BooleanAttr2 AttributeConfig `mapstructure:"boolean_attr2"` + EnumAttr AttributeConfig `mapstructure:"enum_attr"` + MapAttr AttributeConfig `mapstructure:"map_attr"` + OverriddenIntAttr AttributeConfig `mapstructure:"overridden_int_attr"` + SliceAttr AttributeConfig `mapstructure:"slice_attr"` + StringAttr AttributeConfig `mapstructure:"string_attr"` +} + +func DefaultAttributesConfig() AttributesConfig { + return AttributesConfig{ + BooleanAttr: AttributeConfig{ + Enabled: true, + }, + BooleanAttr2: AttributeConfig{ + Enabled: true, + }, + EnumAttr: AttributeConfig{ + Enabled: true, + }, + MapAttr: AttributeConfig{ + Enabled: true, + }, + OverriddenIntAttr: AttributeConfig{ + Enabled: true, + }, + SliceAttr: AttributeConfig{ + Enabled: true, + }, + StringAttr: AttributeConfig{ + Enabled: false, + }, + } +} + func DefaultMetricsConfig() MetricsConfig { return MetricsConfig{ DefaultMetric: MetricConfig{ - Enabled: true, + Enabled: true, + AggregationStrategy: AggregationStrategySum, }, DefaultMetricToBeRemoved: MetricConfig{ - Enabled: true, + Enabled: true, + AggregationStrategy: AggregationStrategySum, }, MetricInputType: MetricConfig{ - Enabled: true, + Enabled: true, + AggregationStrategy: AggregationStrategySum, }, OptionalMetric: MetricConfig{ - Enabled: false, + Enabled: false, + AggregationStrategy: AggregationStrategyAvg, }, OptionalMetricEmptyUnit: MetricConfig{ - Enabled: false, + Enabled: false, + AggregationStrategy: AggregationStrategyAvg, }, } } @@ -125,12 +184,14 @@ func DefaultResourceAttributesConfig() ResourceAttributesConfig { // MetricsBuilderConfig is a configuration for sample metrics builder. type MetricsBuilderConfig struct { Metrics MetricsConfig `mapstructure:"metrics"` + Attributes AttributesConfig `mapstructure:"attributes"` ResourceAttributes ResourceAttributesConfig `mapstructure:"resource_attributes"` } func DefaultMetricsBuilderConfig() MetricsBuilderConfig { return MetricsBuilderConfig{ Metrics: DefaultMetricsConfig(), + Attributes: DefaultAttributesConfig(), ResourceAttributes: DefaultResourceAttributesConfig(), } } diff --git a/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_config_test.go b/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_config_test.go index cc1a2cd0525..94bd127bccd 100644 --- a/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_config_test.go +++ b/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_config_test.go @@ -27,11 +27,42 @@ func TestMetricsBuilderConfig(t *testing.T) { name: "all_set", want: MetricsBuilderConfig{ Metrics: MetricsConfig{ - DefaultMetric: MetricConfig{Enabled: true}, - DefaultMetricToBeRemoved: MetricConfig{Enabled: true}, - MetricInputType: MetricConfig{Enabled: true}, - OptionalMetric: MetricConfig{Enabled: true}, - OptionalMetricEmptyUnit: MetricConfig{Enabled: true}, + DefaultMetric: MetricConfig{ + Enabled: true, + AggregationStrategy: "sum"}, + DefaultMetricToBeRemoved: MetricConfig{ + Enabled: true, + AggregationStrategy: "sum"}, + MetricInputType: MetricConfig{ + Enabled: true, + AggregationStrategy: "sum"}, + OptionalMetric: MetricConfig{ + Enabled: true, AggregationStrategy: "avg"}, + OptionalMetricEmptyUnit: MetricConfig{ + Enabled: true, AggregationStrategy: "avg"}, + }, + Attributes: AttributesConfig{ + BooleanAttr: AttributeConfig{ + Enabled: true, + }, + BooleanAttr2: AttributeConfig{ + Enabled: true, + }, + EnumAttr: AttributeConfig{ + Enabled: true, + }, + MapAttr: AttributeConfig{ + Enabled: true, + }, + OverriddenIntAttr: AttributeConfig{ + Enabled: true, + }, + SliceAttr: AttributeConfig{ + Enabled: true, + }, + StringAttr: AttributeConfig{ + Enabled: true, + }, }, ResourceAttributes: ResourceAttributesConfig{ MapResourceAttr: ResourceAttributeConfig{Enabled: true}, @@ -49,11 +80,42 @@ func TestMetricsBuilderConfig(t *testing.T) { name: "none_set", want: MetricsBuilderConfig{ Metrics: MetricsConfig{ - DefaultMetric: MetricConfig{Enabled: false}, - DefaultMetricToBeRemoved: MetricConfig{Enabled: false}, - MetricInputType: MetricConfig{Enabled: false}, - OptionalMetric: MetricConfig{Enabled: false}, - OptionalMetricEmptyUnit: MetricConfig{Enabled: false}, + DefaultMetric: MetricConfig{ + Enabled: false, + AggregationStrategy: "sum"}, + DefaultMetricToBeRemoved: MetricConfig{ + Enabled: false, + AggregationStrategy: "sum"}, + MetricInputType: MetricConfig{ + Enabled: false, + AggregationStrategy: "sum"}, + OptionalMetric: MetricConfig{ + Enabled: false, AggregationStrategy: "avg"}, + OptionalMetricEmptyUnit: MetricConfig{ + Enabled: false, AggregationStrategy: "avg"}, + }, + Attributes: AttributesConfig{ + BooleanAttr: AttributeConfig{ + Enabled: false, + }, + BooleanAttr2: AttributeConfig{ + Enabled: false, + }, + EnumAttr: AttributeConfig{ + Enabled: false, + }, + MapAttr: AttributeConfig{ + Enabled: false, + }, + OverriddenIntAttr: AttributeConfig{ + Enabled: false, + }, + SliceAttr: AttributeConfig{ + Enabled: false, + }, + StringAttr: AttributeConfig{ + Enabled: false, + }, }, ResourceAttributes: ResourceAttributesConfig{ MapResourceAttr: ResourceAttributeConfig{Enabled: false}, @@ -87,6 +149,61 @@ func loadMetricsBuilderConfig(t *testing.T, name string) MetricsBuilderConfig { return cfg } +func TestAttributesConfig(t *testing.T) { + tests := []struct { + name string + want AttributesConfig + }{ + { + name: "default", + want: DefaultAttributesConfig(), + }, + { + name: "all_set", + want: AttributesConfig{ + BooleanAttr: AttributeConfig{Enabled: true}, + BooleanAttr2: AttributeConfig{Enabled: true}, + EnumAttr: AttributeConfig{Enabled: true}, + MapAttr: AttributeConfig{Enabled: true}, + OverriddenIntAttr: AttributeConfig{Enabled: true}, + SliceAttr: AttributeConfig{Enabled: true}, + StringAttr: AttributeConfig{Enabled: true}, + }, + }, + { + name: "none_set", + want: AttributesConfig{ + BooleanAttr: AttributeConfig{Enabled: false}, + BooleanAttr2: AttributeConfig{Enabled: false}, + EnumAttr: AttributeConfig{Enabled: false}, + MapAttr: AttributeConfig{Enabled: false}, + OverriddenIntAttr: AttributeConfig{Enabled: false}, + SliceAttr: AttributeConfig{Enabled: false}, + StringAttr: AttributeConfig{Enabled: false}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := loadAttributesConfig(t, tt.name) + diff := cmp.Diff(tt.want, cfg, cmpopts.IgnoreUnexported(AttributeConfig{})) + require.Emptyf(t, diff, "Config mismatch (-expected +actual):\n%s", diff) + }) + } +} + +func loadAttributesConfig(t *testing.T, name string) AttributesConfig { + cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) + require.NoError(t, err) + sub, err := cm.Sub(name) + require.NoError(t, err) + sub, err = sub.Sub("attributes") + require.NoError(t, err) + cfg := DefaultAttributesConfig() + require.NoError(t, sub.Unmarshal(&cfg)) + return cfg +} + func TestResourceAttributesConfig(t *testing.T) { tests := []struct { name string diff --git a/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_metrics.go b/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_metrics.go index 475e719db5c..7d00bdf1f38 100644 --- a/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_metrics.go +++ b/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_metrics.go @@ -20,7 +20,11 @@ import ( type AttributeEnumAttr int const ( - _ AttributeEnumAttr = iota + AggregationStrategySum = "sum" + AggregationStrategyAvg = "avg" + AggregationStrategyMin = "min" + AggregationStrategyMax = "max" + _ AttributeEnumAttr = iota AttributeEnumAttrRed AttributeEnumAttrGreen AttributeEnumAttrBlue @@ -77,13 +81,16 @@ type metricInfo struct { } type metricDefaultMetric struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []int64 // slice containing number of aggregated datapoints at each index } // init fills default.metric metric with initial data. func (m *metricDefaultMetric) init() { + m.aggDataPoints = nil m.data.SetName("default.metric") m.data.SetDescription("Monotonic cumulative sum int metric enabled by default.") m.data.SetUnit("s") @@ -97,15 +104,53 @@ func (m *metricDefaultMetric) recordDataPoint(start pcommon.Timestamp, ts pcommo if !m.config.Enabled { return } - dp := m.data.Sum().DataPoints().AppendEmpty() + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) + + if m.attributes.StringAttr.Enabled { + dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) + } + if m.attributes.OverriddenIntAttr.Enabled { + dp.Attributes().PutInt("state", overriddenIntAttrAttributeValue) + } + if m.attributes.EnumAttr.Enabled { + dp.Attributes().PutStr("enum_attr", enumAttrAttributeValue) + } + if m.attributes.SliceAttr.Enabled { + dp.Attributes().PutEmptySlice("slice_attr").FromRaw(sliceAttrAttributeValue) + } + if m.attributes.MapAttr.Enabled { + dp.Attributes().PutEmptyMap("map_attr").FromRaw(mapAttrAttributeValue) + } + + var s string + dps := m.data.Sum().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.SetIntValue(dpi.IntValue() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.IntValue() > val { + dpi.SetIntValue(val) + } + return + case AggregationStrategyMax: + if dpi.IntValue() < val { + dpi.SetIntValue(val) + } + return + } + } + } + dp.SetIntValue(val) - dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) - dp.Attributes().PutInt("state", overriddenIntAttrAttributeValue) - dp.Attributes().PutStr("enum_attr", enumAttrAttributeValue) - dp.Attributes().PutEmptySlice("slice_attr").FromRaw(sliceAttrAttributeValue) - dp.Attributes().PutEmptyMap("map_attr").FromRaw(mapAttrAttributeValue) + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -118,14 +163,19 @@ func (m *metricDefaultMetric) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metricDefaultMetric) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.Sum().DataPoints().At(i).SetIntValue(m.data.Sum().DataPoints().At(i).IntValue() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetricDefaultMetric(cfg MetricConfig) metricDefaultMetric { - m := metricDefaultMetric{config: cfg} +func newMetricDefaultMetric(cfg MetricConfig, acfg AttributesConfig) metricDefaultMetric { + m := metricDefaultMetric{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -134,13 +184,16 @@ func newMetricDefaultMetric(cfg MetricConfig) metricDefaultMetric { } type metricDefaultMetricToBeRemoved struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []float64 // slice containing number of aggregated datapoints at each index } // init fills default.metric.to_be_removed metric with initial data. func (m *metricDefaultMetricToBeRemoved) init() { + m.aggDataPoints = nil m.data.SetName("default.metric.to_be_removed") m.data.SetDescription("[DEPRECATED] Non-monotonic delta sum double metric enabled by default.") m.data.SetUnit("s") @@ -153,10 +206,37 @@ func (m *metricDefaultMetricToBeRemoved) recordDataPoint(start pcommon.Timestamp if !m.config.Enabled { return } - dp := m.data.Sum().DataPoints().AppendEmpty() + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) + + var s string + dps := m.data.Sum().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.SetDoubleValue(dpi.DoubleValue() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.DoubleValue() > val { + dpi.SetDoubleValue(val) + } + return + case AggregationStrategyMax: + if dpi.DoubleValue() < val { + dpi.SetDoubleValue(val) + } + return + } + } + } + dp.SetDoubleValue(val) + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -169,14 +249,19 @@ func (m *metricDefaultMetricToBeRemoved) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metricDefaultMetricToBeRemoved) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.Sum().DataPoints().At(i).SetDoubleValue(m.data.Sum().DataPoints().At(i).DoubleValue() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetricDefaultMetricToBeRemoved(cfg MetricConfig) metricDefaultMetricToBeRemoved { - m := metricDefaultMetricToBeRemoved{config: cfg} +func newMetricDefaultMetricToBeRemoved(cfg MetricConfig, acfg AttributesConfig) metricDefaultMetricToBeRemoved { + m := metricDefaultMetricToBeRemoved{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -185,13 +270,16 @@ func newMetricDefaultMetricToBeRemoved(cfg MetricConfig) metricDefaultMetricToBe } type metricMetricInputType struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []int64 // slice containing number of aggregated datapoints at each index } // init fills metric.input_type metric with initial data. func (m *metricMetricInputType) init() { + m.aggDataPoints = nil m.data.SetName("metric.input_type") m.data.SetDescription("Monotonic cumulative sum int metric with string input_type enabled by default.") m.data.SetUnit("s") @@ -205,15 +293,53 @@ func (m *metricMetricInputType) recordDataPoint(start pcommon.Timestamp, ts pcom if !m.config.Enabled { return } - dp := m.data.Sum().DataPoints().AppendEmpty() + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) + + if m.attributes.StringAttr.Enabled { + dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) + } + if m.attributes.OverriddenIntAttr.Enabled { + dp.Attributes().PutInt("state", overriddenIntAttrAttributeValue) + } + if m.attributes.EnumAttr.Enabled { + dp.Attributes().PutStr("enum_attr", enumAttrAttributeValue) + } + if m.attributes.SliceAttr.Enabled { + dp.Attributes().PutEmptySlice("slice_attr").FromRaw(sliceAttrAttributeValue) + } + if m.attributes.MapAttr.Enabled { + dp.Attributes().PutEmptyMap("map_attr").FromRaw(mapAttrAttributeValue) + } + + var s string + dps := m.data.Sum().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.SetIntValue(dpi.IntValue() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.IntValue() > val { + dpi.SetIntValue(val) + } + return + case AggregationStrategyMax: + if dpi.IntValue() < val { + dpi.SetIntValue(val) + } + return + } + } + } + dp.SetIntValue(val) - dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) - dp.Attributes().PutInt("state", overriddenIntAttrAttributeValue) - dp.Attributes().PutStr("enum_attr", enumAttrAttributeValue) - dp.Attributes().PutEmptySlice("slice_attr").FromRaw(sliceAttrAttributeValue) - dp.Attributes().PutEmptyMap("map_attr").FromRaw(mapAttrAttributeValue) + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -226,14 +352,19 @@ func (m *metricMetricInputType) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metricMetricInputType) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.Sum().DataPoints().At(i).SetIntValue(m.data.Sum().DataPoints().At(i).IntValue() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetricMetricInputType(cfg MetricConfig) metricMetricInputType { - m := metricMetricInputType{config: cfg} +func newMetricMetricInputType(cfg MetricConfig, acfg AttributesConfig) metricMetricInputType { + m := metricMetricInputType{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -242,13 +373,16 @@ func newMetricMetricInputType(cfg MetricConfig) metricMetricInputType { } type metricOptionalMetric struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []float64 // slice containing number of aggregated datapoints at each index } // init fills optional.metric metric with initial data. func (m *metricOptionalMetric) init() { + m.aggDataPoints = nil m.data.SetName("optional.metric") m.data.SetDescription("[DEPRECATED] Gauge double metric disabled by default.") m.data.SetUnit("1") @@ -260,13 +394,47 @@ func (m *metricOptionalMetric) recordDataPoint(start pcommon.Timestamp, ts pcomm if !m.config.Enabled { return } - dp := m.data.Gauge().DataPoints().AppendEmpty() + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) + + if m.attributes.StringAttr.Enabled { + dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) + } + if m.attributes.BooleanAttr.Enabled { + dp.Attributes().PutBool("boolean_attr", booleanAttrAttributeValue) + } + if m.attributes.BooleanAttr2.Enabled { + dp.Attributes().PutBool("boolean_attr2", booleanAttr2AttributeValue) + } + + var s string + dps := m.data.Gauge().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.SetDoubleValue(dpi.DoubleValue() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.DoubleValue() > val { + dpi.SetDoubleValue(val) + } + return + case AggregationStrategyMax: + if dpi.DoubleValue() < val { + dpi.SetDoubleValue(val) + } + return + } + } + } + dp.SetDoubleValue(val) - dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) - dp.Attributes().PutBool("boolean_attr", booleanAttrAttributeValue) - dp.Attributes().PutBool("boolean_attr2", booleanAttr2AttributeValue) + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -279,14 +447,19 @@ func (m *metricOptionalMetric) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metricOptionalMetric) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.Gauge().DataPoints().At(i).SetDoubleValue(m.data.Gauge().DataPoints().At(i).DoubleValue() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetricOptionalMetric(cfg MetricConfig) metricOptionalMetric { - m := metricOptionalMetric{config: cfg} +func newMetricOptionalMetric(cfg MetricConfig, acfg AttributesConfig) metricOptionalMetric { + m := metricOptionalMetric{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -295,13 +468,16 @@ func newMetricOptionalMetric(cfg MetricConfig) metricOptionalMetric { } type metricOptionalMetricEmptyUnit struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []float64 // slice containing number of aggregated datapoints at each index } // init fills optional.metric.empty_unit metric with initial data. func (m *metricOptionalMetricEmptyUnit) init() { + m.aggDataPoints = nil m.data.SetName("optional.metric.empty_unit") m.data.SetDescription("[DEPRECATED] Gauge double metric disabled by default.") m.data.SetUnit("") @@ -313,12 +489,44 @@ func (m *metricOptionalMetricEmptyUnit) recordDataPoint(start pcommon.Timestamp, if !m.config.Enabled { return } - dp := m.data.Gauge().DataPoints().AppendEmpty() + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) + + if m.attributes.StringAttr.Enabled { + dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) + } + if m.attributes.BooleanAttr.Enabled { + dp.Attributes().PutBool("boolean_attr", booleanAttrAttributeValue) + } + + var s string + dps := m.data.Gauge().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.SetDoubleValue(dpi.DoubleValue() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.DoubleValue() > val { + dpi.SetDoubleValue(val) + } + return + case AggregationStrategyMax: + if dpi.DoubleValue() < val { + dpi.SetDoubleValue(val) + } + return + } + } + } + dp.SetDoubleValue(val) - dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) - dp.Attributes().PutBool("boolean_attr", booleanAttrAttributeValue) + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -331,14 +539,19 @@ func (m *metricOptionalMetricEmptyUnit) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metricOptionalMetricEmptyUnit) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.Gauge().DataPoints().At(i).SetDoubleValue(m.data.Gauge().DataPoints().At(i).DoubleValue() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetricOptionalMetricEmptyUnit(cfg MetricConfig) metricOptionalMetricEmptyUnit { - m := metricOptionalMetricEmptyUnit{config: cfg} +func newMetricOptionalMetricEmptyUnit(cfg MetricConfig, acfg AttributesConfig) metricOptionalMetricEmptyUnit { + m := metricOptionalMetricEmptyUnit{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -407,11 +620,11 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings connector.Settings, op startTime: pcommon.NewTimestampFromTime(time.Now()), metricsBuffer: pmetric.NewMetrics(), buildInfo: settings.BuildInfo, - metricDefaultMetric: newMetricDefaultMetric(mbc.Metrics.DefaultMetric), - metricDefaultMetricToBeRemoved: newMetricDefaultMetricToBeRemoved(mbc.Metrics.DefaultMetricToBeRemoved), - metricMetricInputType: newMetricMetricInputType(mbc.Metrics.MetricInputType), - metricOptionalMetric: newMetricOptionalMetric(mbc.Metrics.OptionalMetric), - metricOptionalMetricEmptyUnit: newMetricOptionalMetricEmptyUnit(mbc.Metrics.OptionalMetricEmptyUnit), + metricDefaultMetric: newMetricDefaultMetric(mbc.Metrics.DefaultMetric, mbc.Attributes), + metricDefaultMetricToBeRemoved: newMetricDefaultMetricToBeRemoved(mbc.Metrics.DefaultMetricToBeRemoved, mbc.Attributes), + metricMetricInputType: newMetricMetricInputType(mbc.Metrics.MetricInputType, mbc.Attributes), + metricOptionalMetric: newMetricOptionalMetric(mbc.Metrics.OptionalMetric, mbc.Attributes), + metricOptionalMetricEmptyUnit: newMetricOptionalMetricEmptyUnit(mbc.Metrics.OptionalMetricEmptyUnit, mbc.Attributes), resourceAttributeIncludeFilter: make(map[string]filter.Filter), resourceAttributeExcludeFilter: make(map[string]filter.Filter), } @@ -572,11 +785,13 @@ func (mb *MetricsBuilder) Emit(options ...ResourceMetricsOption) pmetric.Metrics // RecordDefaultMetricDataPoint adds a data point to default.metric metric. func (mb *MetricsBuilder) RecordDefaultMetricDataPoint(ts pcommon.Timestamp, val int64, stringAttrAttributeValue string, overriddenIntAttrAttributeValue int64, enumAttrAttributeValue AttributeEnumAttr, sliceAttrAttributeValue []any, mapAttrAttributeValue map[string]any) { mb.metricDefaultMetric.recordDataPoint(mb.startTime, ts, val, stringAttrAttributeValue, overriddenIntAttrAttributeValue, enumAttrAttributeValue.String(), sliceAttrAttributeValue, mapAttrAttributeValue) + return } // RecordDefaultMetricToBeRemovedDataPoint adds a data point to default.metric.to_be_removed metric. func (mb *MetricsBuilder) RecordDefaultMetricToBeRemovedDataPoint(ts pcommon.Timestamp, val float64) { mb.metricDefaultMetricToBeRemoved.recordDataPoint(mb.startTime, ts, val) + return } // RecordMetricInputTypeDataPoint adds a data point to metric.input_type metric. @@ -592,11 +807,13 @@ func (mb *MetricsBuilder) RecordMetricInputTypeDataPoint(ts pcommon.Timestamp, i // RecordOptionalMetricDataPoint adds a data point to optional.metric metric. func (mb *MetricsBuilder) RecordOptionalMetricDataPoint(ts pcommon.Timestamp, val float64, stringAttrAttributeValue string, booleanAttrAttributeValue bool, booleanAttr2AttributeValue bool) { mb.metricOptionalMetric.recordDataPoint(mb.startTime, ts, val, stringAttrAttributeValue, booleanAttrAttributeValue, booleanAttr2AttributeValue) + return } // RecordOptionalMetricEmptyUnitDataPoint adds a data point to optional.metric.empty_unit metric. func (mb *MetricsBuilder) RecordOptionalMetricEmptyUnitDataPoint(ts pcommon.Timestamp, val float64, stringAttrAttributeValue string, booleanAttrAttributeValue bool) { mb.metricOptionalMetricEmptyUnit.recordDataPoint(mb.startTime, ts, val, stringAttrAttributeValue, booleanAttrAttributeValue) + return } // Reset resets metrics builder to its initial state. It should be used when external metrics source is restarted, diff --git a/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_metrics_test.go b/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_metrics_test.go index 32e55353c2c..bfb217bf8c0 100644 --- a/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_metrics_test.go +++ b/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_metrics_test.go @@ -100,6 +100,8 @@ func TestMetricsBuilder(t *testing.T) { defaultMetricsCount++ allMetricsCount++ mb.RecordDefaultMetricDataPoint(ts, 1, "string_attr-val", 19, AttributeEnumAttrRed, []any{"slice_attr-item1", "slice_attr-item2"}, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}) + allMetricsCount++ + mb.RecordDefaultMetricDataPoint(ts, 3, "string_attr-val-2", 19, AttributeEnumAttrRed, []any{"slice_attr-item1", "slice_attr-item2"}, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}) defaultMetricsCount++ allMetricsCount++ @@ -108,12 +110,18 @@ func TestMetricsBuilder(t *testing.T) { defaultMetricsCount++ allMetricsCount++ mb.RecordMetricInputTypeDataPoint(ts, "1", "string_attr-val", 19, AttributeEnumAttrRed, []any{"slice_attr-item1", "slice_attr-item2"}, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}) + allMetricsCount++ + mb.RecordMetricInputTypeDataPoint(ts, "3", "string_attr-val-2", 19, AttributeEnumAttrRed, []any{"slice_attr-item1", "slice_attr-item2"}, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}) allMetricsCount++ mb.RecordOptionalMetricDataPoint(ts, 1, "string_attr-val", true, false) + allMetricsCount++ + mb.RecordOptionalMetricDataPoint(ts, 3, "string_attr-val-2", true, false) allMetricsCount++ mb.RecordOptionalMetricEmptyUnitDataPoint(ts, 1, "string_attr-val", true) + allMetricsCount++ + mb.RecordOptionalMetricEmptyUnitDataPoint(ts, 3, "string_attr-val-2", true) rb := mb.NewResourceBuilder() rb.SetMapResourceAttr(map[string]any{"key1": "map.resource.attr-val1", "key2": "map.resource.attr-val2"}) @@ -141,7 +149,7 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, defaultMetricsCount, ms.Len()) } if tt.metricsSet == testDataSetAll { - assert.Equal(t, allMetricsCount, ms.Len()) + assert.Equal(t, (allMetricsCount/2)+1, ms.Len()) } validatedMetrics := make(map[string]bool) for i := 0; i < ms.Len(); i++ { @@ -150,113 +158,193 @@ func TestMetricsBuilder(t *testing.T) { assert.False(t, validatedMetrics["default.metric"], "Found a duplicate in the metrics slice: default.metric") validatedMetrics["default.metric"] = true assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) - assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + dp := ms.At(i).Sum().DataPoints().At(0) + if tt.name == "all_set" { + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + } else { + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(4), dp.IntValue()) + } assert.Equal(t, "Monotonic cumulative sum int metric enabled by default.", ms.At(i).Description()) assert.Equal(t, "s", ms.At(i).Unit()) assert.True(t, ms.At(i).Sum().IsMonotonic()) assert.Equal(t, pmetric.AggregationTemporalityCumulative, ms.At(i).Sum().AggregationTemporality()) - dp := ms.At(i).Sum().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) - assert.Equal(t, int64(1), dp.IntValue()) attrVal, ok := dp.Attributes().Get("string_attr") - assert.True(t, ok) - assert.Equal(t, "string_attr-val", attrVal.Str()) + if !mb.config.Attributes.StringAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "string_attr-val", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("state") - assert.True(t, ok) - assert.EqualValues(t, 19, attrVal.Int()) + if !mb.config.Attributes.OverriddenIntAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.EqualValues(t, 19, attrVal.Int()) + } attrVal, ok = dp.Attributes().Get("enum_attr") - assert.True(t, ok) - assert.Equal(t, "red", attrVal.Str()) + if !mb.config.Attributes.EnumAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "red", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("slice_attr") - assert.True(t, ok) - assert.Equal(t, []any{"slice_attr-item1", "slice_attr-item2"}, attrVal.Slice().AsRaw()) + if !mb.config.Attributes.SliceAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, []any{"slice_attr-item1", "slice_attr-item2"}, attrVal.Slice().AsRaw()) + } attrVal, ok = dp.Attributes().Get("map_attr") - assert.True(t, ok) - assert.Equal(t, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}, attrVal.Map().AsRaw()) + if !mb.config.Attributes.MapAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}, attrVal.Map().AsRaw()) + } case "default.metric.to_be_removed": assert.False(t, validatedMetrics["default.metric.to_be_removed"], "Found a duplicate in the metrics slice: default.metric.to_be_removed") validatedMetrics["default.metric.to_be_removed"] = true assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + dp := ms.At(i).Sum().DataPoints().At(0) assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) assert.Equal(t, "[DEPRECATED] Non-monotonic delta sum double metric enabled by default.", ms.At(i).Description()) assert.Equal(t, "s", ms.At(i).Unit()) assert.False(t, ms.At(i).Sum().IsMonotonic()) assert.Equal(t, pmetric.AggregationTemporalityDelta, ms.At(i).Sum().AggregationTemporality()) - dp := ms.At(i).Sum().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) - assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) case "metric.input_type": assert.False(t, validatedMetrics["metric.input_type"], "Found a duplicate in the metrics slice: metric.input_type") validatedMetrics["metric.input_type"] = true assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) - assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + dp := ms.At(i).Sum().DataPoints().At(0) + if tt.name == "all_set" { + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + } else { + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(4), dp.IntValue()) + } assert.Equal(t, "Monotonic cumulative sum int metric with string input_type enabled by default.", ms.At(i).Description()) assert.Equal(t, "s", ms.At(i).Unit()) assert.True(t, ms.At(i).Sum().IsMonotonic()) assert.Equal(t, pmetric.AggregationTemporalityCumulative, ms.At(i).Sum().AggregationTemporality()) - dp := ms.At(i).Sum().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) - assert.Equal(t, int64(1), dp.IntValue()) attrVal, ok := dp.Attributes().Get("string_attr") - assert.True(t, ok) - assert.Equal(t, "string_attr-val", attrVal.Str()) + if !mb.config.Attributes.StringAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "string_attr-val", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("state") - assert.True(t, ok) - assert.EqualValues(t, 19, attrVal.Int()) + if !mb.config.Attributes.OverriddenIntAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.EqualValues(t, 19, attrVal.Int()) + } attrVal, ok = dp.Attributes().Get("enum_attr") - assert.True(t, ok) - assert.Equal(t, "red", attrVal.Str()) + if !mb.config.Attributes.EnumAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "red", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("slice_attr") - assert.True(t, ok) - assert.Equal(t, []any{"slice_attr-item1", "slice_attr-item2"}, attrVal.Slice().AsRaw()) + if !mb.config.Attributes.SliceAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, []any{"slice_attr-item1", "slice_attr-item2"}, attrVal.Slice().AsRaw()) + } attrVal, ok = dp.Attributes().Get("map_attr") - assert.True(t, ok) - assert.Equal(t, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}, attrVal.Map().AsRaw()) + if !mb.config.Attributes.MapAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}, attrVal.Map().AsRaw()) + } case "optional.metric": assert.False(t, validatedMetrics["optional.metric"], "Found a duplicate in the metrics slice: optional.metric") validatedMetrics["optional.metric"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) - assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + dp := ms.At(i).Gauge().DataPoints().At(0) + if tt.name == "all_set" { + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + } else { + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(4), dp.DoubleValue(), 0.01) + } assert.Equal(t, "[DEPRECATED] Gauge double metric disabled by default.", ms.At(i).Description()) assert.Equal(t, "1", ms.At(i).Unit()) - dp := ms.At(i).Gauge().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) - assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) attrVal, ok := dp.Attributes().Get("string_attr") - assert.True(t, ok) - assert.Equal(t, "string_attr-val", attrVal.Str()) + if !mb.config.Attributes.StringAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "string_attr-val", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("boolean_attr") - assert.True(t, ok) - assert.True(t, attrVal.Bool()) + if !mb.config.Attributes.BooleanAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.True(t, attrVal.Bool()) + } attrVal, ok = dp.Attributes().Get("boolean_attr2") - assert.True(t, ok) - assert.False(t, attrVal.Bool()) + if !mb.config.Attributes.BooleanAttr2.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.False(t, attrVal.Bool()) + } case "optional.metric.empty_unit": assert.False(t, validatedMetrics["optional.metric.empty_unit"], "Found a duplicate in the metrics slice: optional.metric.empty_unit") validatedMetrics["optional.metric.empty_unit"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) - assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + dp := ms.At(i).Gauge().DataPoints().At(0) + if tt.name == "all_set" { + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + } else { + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(4), dp.DoubleValue(), 0.01) + } assert.Equal(t, "[DEPRECATED] Gauge double metric disabled by default.", ms.At(i).Description()) assert.Empty(t, ms.At(i).Unit()) - dp := ms.At(i).Gauge().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) - assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) attrVal, ok := dp.Attributes().Get("string_attr") - assert.True(t, ok) - assert.Equal(t, "string_attr-val", attrVal.Str()) + if !mb.config.Attributes.StringAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "string_attr-val", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("boolean_attr") - assert.True(t, ok) - assert.True(t, attrVal.Bool()) + if !mb.config.Attributes.BooleanAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.True(t, attrVal.Bool()) + } } } }) diff --git a/cmd/mdatagen/internal/sampleconnector/internal/metadata/testdata/config.yaml b/cmd/mdatagen/internal/sampleconnector/internal/metadata/testdata/config.yaml index c4b7edd6568..9926e120aac 100644 --- a/cmd/mdatagen/internal/sampleconnector/internal/metadata/testdata/config.yaml +++ b/cmd/mdatagen/internal/sampleconnector/internal/metadata/testdata/config.yaml @@ -1,5 +1,20 @@ default: all_set: + attributes: + boolean_attr: + enabled: true + boolean_attr2: + enabled: true + enum_attr: + enabled: true + map_attr: + enabled: true + overridden_int_attr: + enabled: true + slice_attr: + enabled: true + string_attr: + enabled: true metrics: default.metric: enabled: true @@ -29,6 +44,21 @@ all_set: string.resource.attr_to_be_removed: enabled: true none_set: + attributes: + boolean_attr: + enabled: false + boolean_attr2: + enabled: false + enum_attr: + enabled: false + map_attr: + enabled: false + overridden_int_attr: + enabled: false + slice_attr: + enabled: false + string_attr: + enabled: false metrics: default.metric: enabled: false diff --git a/cmd/mdatagen/internal/sampleconnector/metadata.yaml b/cmd/mdatagen/internal/sampleconnector/metadata.yaml index ad46b6f834d..f701afe78f2 100644 --- a/cmd/mdatagen/internal/sampleconnector/metadata.yaml +++ b/cmd/mdatagen/internal/sampleconnector/metadata.yaml @@ -69,34 +69,41 @@ attributes: string_attr: description: Attribute with any string value. type: string + enabled: false overridden_int_attr: name_override: state description: Integer attribute with overridden name. type: int + enabled: true enum_attr: description: Attribute with a known set of string values. type: string enum: [red, green, blue] + enabled: true boolean_attr: description: Attribute with a boolean value. type: bool + enabled: true # This 2nd boolean attribute allows us to test both values for boolean attributes, # as test values are based on the parity of the attribute name length. boolean_attr2: description: Another attribute with a boolean value. type: bool + enabled: true slice_attr: description: Attribute with a slice value. type: slice + enabled: true map_attr: description: Attribute with a map value. type: map + enabled: true metrics: default.metric: diff --git a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config.go b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config.go index 4668abbc732..0e56f0add8f 100644 --- a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config.go +++ b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config.go @@ -3,13 +3,16 @@ package metadata import ( + "fmt" + "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/filter" ) // MetricConfig provides common config for a particular metric. type MetricConfig struct { - Enabled bool `mapstructure:"enabled"` + Enabled bool `mapstructure:"enabled"` + AggregationStrategy string `mapstructure:"aggregation_strategy"` enabledSetByUser bool } @@ -22,10 +25,23 @@ func (ms *MetricConfig) Unmarshal(parser *confmap.Conf) error { if err != nil { return err } + + if ms.AggregationStrategy != AggregationStrategySum && + ms.AggregationStrategy != AggregationStrategyAvg && + ms.AggregationStrategy != AggregationStrategyMin && + ms.AggregationStrategy != AggregationStrategyMax { + return fmt.Errorf("invalid aggregation strategy set: '%v'", ms.AggregationStrategy) + } + ms.enabledSetByUser = parser.IsSet("enabled") return nil } +// AttributeConfig holds configuration information for a particular metric. +type AttributeConfig struct { + Enabled bool `mapstructure:"enabled"` +} + // MetricsConfig provides config for sample metrics. type MetricsConfig struct { DefaultMetric MetricConfig `mapstructure:"default.metric"` @@ -35,22 +51,73 @@ type MetricsConfig struct { OptionalMetricEmptyUnit MetricConfig `mapstructure:"optional.metric.empty_unit"` } +// AttributesConfig is the collected configuration for all attributes in the +// component +type AttributesConfig struct { + BooleanAttr AttributeConfig `mapstructure:"boolean_attr"` + BooleanAttr2 AttributeConfig `mapstructure:"boolean_attr2"` + EnumAttr AttributeConfig `mapstructure:"enum_attr"` + MapAttr AttributeConfig `mapstructure:"map_attr"` + OptionalIntAttr AttributeConfig `mapstructure:"optional_int_attr"` + OptionalStringAttr AttributeConfig `mapstructure:"optional_string_attr"` + OverriddenIntAttr AttributeConfig `mapstructure:"overridden_int_attr"` + SliceAttr AttributeConfig `mapstructure:"slice_attr"` + StringAttr AttributeConfig `mapstructure:"string_attr"` +} + +func DefaultAttributesConfig() AttributesConfig { + return AttributesConfig{ + BooleanAttr: AttributeConfig{ + Enabled: true, + }, + BooleanAttr2: AttributeConfig{ + Enabled: true, + }, + EnumAttr: AttributeConfig{ + Enabled: true, + }, + MapAttr: AttributeConfig{ + Enabled: true, + }, + OptionalIntAttr: AttributeConfig{ + Enabled: true, + }, + OptionalStringAttr: AttributeConfig{ + Enabled: true, + }, + OverriddenIntAttr: AttributeConfig{ + Enabled: true, + }, + SliceAttr: AttributeConfig{ + Enabled: true, + }, + StringAttr: AttributeConfig{ + Enabled: false, + }, + } +} + func DefaultMetricsConfig() MetricsConfig { return MetricsConfig{ DefaultMetric: MetricConfig{ - Enabled: true, + Enabled: true, + AggregationStrategy: AggregationStrategySum, }, DefaultMetricToBeRemoved: MetricConfig{ - Enabled: true, + Enabled: true, + AggregationStrategy: AggregationStrategySum, }, MetricInputType: MetricConfig{ - Enabled: true, + Enabled: true, + AggregationStrategy: AggregationStrategySum, }, OptionalMetric: MetricConfig{ - Enabled: false, + Enabled: false, + AggregationStrategy: AggregationStrategyAvg, }, OptionalMetricEmptyUnit: MetricConfig{ - Enabled: false, + Enabled: false, + AggregationStrategy: AggregationStrategyAvg, }, } } @@ -172,12 +239,14 @@ func DefaultResourceAttributesConfig() ResourceAttributesConfig { // MetricsBuilderConfig is a configuration for sample metrics builder. type MetricsBuilderConfig struct { Metrics MetricsConfig `mapstructure:"metrics"` + Attributes AttributesConfig `mapstructure:"attributes"` ResourceAttributes ResourceAttributesConfig `mapstructure:"resource_attributes"` } func DefaultMetricsBuilderConfig() MetricsBuilderConfig { return MetricsBuilderConfig{ Metrics: DefaultMetricsConfig(), + Attributes: DefaultAttributesConfig(), ResourceAttributes: DefaultResourceAttributesConfig(), } } diff --git a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config_test.go b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config_test.go index 8f1e1b3d164..53c0d7e2460 100644 --- a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config_test.go +++ b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config_test.go @@ -27,11 +27,48 @@ func TestMetricsBuilderConfig(t *testing.T) { name: "all_set", want: MetricsBuilderConfig{ Metrics: MetricsConfig{ - DefaultMetric: MetricConfig{Enabled: true}, - DefaultMetricToBeRemoved: MetricConfig{Enabled: true}, - MetricInputType: MetricConfig{Enabled: true}, - OptionalMetric: MetricConfig{Enabled: true}, - OptionalMetricEmptyUnit: MetricConfig{Enabled: true}, + DefaultMetric: MetricConfig{ + Enabled: true, + AggregationStrategy: "sum"}, + DefaultMetricToBeRemoved: MetricConfig{ + Enabled: true, + AggregationStrategy: "sum"}, + MetricInputType: MetricConfig{ + Enabled: true, + AggregationStrategy: "sum"}, + OptionalMetric: MetricConfig{ + Enabled: true, AggregationStrategy: "avg"}, + OptionalMetricEmptyUnit: MetricConfig{ + Enabled: true, AggregationStrategy: "avg"}, + }, + Attributes: AttributesConfig{ + BooleanAttr: AttributeConfig{ + Enabled: true, + }, + BooleanAttr2: AttributeConfig{ + Enabled: true, + }, + EnumAttr: AttributeConfig{ + Enabled: true, + }, + MapAttr: AttributeConfig{ + Enabled: true, + }, + OptionalIntAttr: AttributeConfig{ + Enabled: true, + }, + OptionalStringAttr: AttributeConfig{ + Enabled: true, + }, + OverriddenIntAttr: AttributeConfig{ + Enabled: true, + }, + SliceAttr: AttributeConfig{ + Enabled: true, + }, + StringAttr: AttributeConfig{ + Enabled: true, + }, }, ResourceAttributes: ResourceAttributesConfig{ MapResourceAttr: ResourceAttributeConfig{Enabled: true}, @@ -49,11 +86,48 @@ func TestMetricsBuilderConfig(t *testing.T) { name: "none_set", want: MetricsBuilderConfig{ Metrics: MetricsConfig{ - DefaultMetric: MetricConfig{Enabled: false}, - DefaultMetricToBeRemoved: MetricConfig{Enabled: false}, - MetricInputType: MetricConfig{Enabled: false}, - OptionalMetric: MetricConfig{Enabled: false}, - OptionalMetricEmptyUnit: MetricConfig{Enabled: false}, + DefaultMetric: MetricConfig{ + Enabled: false, + AggregationStrategy: "sum"}, + DefaultMetricToBeRemoved: MetricConfig{ + Enabled: false, + AggregationStrategy: "sum"}, + MetricInputType: MetricConfig{ + Enabled: false, + AggregationStrategy: "sum"}, + OptionalMetric: MetricConfig{ + Enabled: false, AggregationStrategy: "avg"}, + OptionalMetricEmptyUnit: MetricConfig{ + Enabled: false, AggregationStrategy: "avg"}, + }, + Attributes: AttributesConfig{ + BooleanAttr: AttributeConfig{ + Enabled: false, + }, + BooleanAttr2: AttributeConfig{ + Enabled: false, + }, + EnumAttr: AttributeConfig{ + Enabled: false, + }, + MapAttr: AttributeConfig{ + Enabled: false, + }, + OptionalIntAttr: AttributeConfig{ + Enabled: false, + }, + OptionalStringAttr: AttributeConfig{ + Enabled: false, + }, + OverriddenIntAttr: AttributeConfig{ + Enabled: false, + }, + SliceAttr: AttributeConfig{ + Enabled: false, + }, + StringAttr: AttributeConfig{ + Enabled: false, + }, }, ResourceAttributes: ResourceAttributesConfig{ MapResourceAttr: ResourceAttributeConfig{Enabled: false}, @@ -97,6 +171,65 @@ func loadLogsBuilderConfig(t *testing.T, name string) LogsBuilderConfig { return cfg } +func TestAttributesConfig(t *testing.T) { + tests := []struct { + name string + want AttributesConfig + }{ + { + name: "default", + want: DefaultAttributesConfig(), + }, + { + name: "all_set", + want: AttributesConfig{ + BooleanAttr: AttributeConfig{Enabled: true}, + BooleanAttr2: AttributeConfig{Enabled: true}, + EnumAttr: AttributeConfig{Enabled: true}, + MapAttr: AttributeConfig{Enabled: true}, + OptionalIntAttr: AttributeConfig{Enabled: true}, + OptionalStringAttr: AttributeConfig{Enabled: true}, + OverriddenIntAttr: AttributeConfig{Enabled: true}, + SliceAttr: AttributeConfig{Enabled: true}, + StringAttr: AttributeConfig{Enabled: true}, + }, + }, + { + name: "none_set", + want: AttributesConfig{ + BooleanAttr: AttributeConfig{Enabled: false}, + BooleanAttr2: AttributeConfig{Enabled: false}, + EnumAttr: AttributeConfig{Enabled: false}, + MapAttr: AttributeConfig{Enabled: false}, + OptionalIntAttr: AttributeConfig{Enabled: false}, + OptionalStringAttr: AttributeConfig{Enabled: false}, + OverriddenIntAttr: AttributeConfig{Enabled: false}, + SliceAttr: AttributeConfig{Enabled: false}, + StringAttr: AttributeConfig{Enabled: false}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := loadAttributesConfig(t, tt.name) + diff := cmp.Diff(tt.want, cfg, cmpopts.IgnoreUnexported(AttributeConfig{})) + require.Emptyf(t, diff, "Config mismatch (-expected +actual):\n%s", diff) + }) + } +} + +func loadAttributesConfig(t *testing.T, name string) AttributesConfig { + cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) + require.NoError(t, err) + sub, err := cm.Sub(name) + require.NoError(t, err) + sub, err = sub.Sub("attributes") + require.NoError(t, err) + cfg := DefaultAttributesConfig() + require.NoError(t, sub.Unmarshal(&cfg)) + return cfg +} + func TestResourceAttributesConfig(t *testing.T) { tests := []struct { name string diff --git a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics.go b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics.go index 6f7473f89c8..034aed4a749 100644 --- a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics.go +++ b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics.go @@ -20,7 +20,11 @@ import ( type AttributeEnumAttr int const ( - _ AttributeEnumAttr = iota + AggregationStrategySum = "sum" + AggregationStrategyAvg = "avg" + AggregationStrategyMin = "min" + AggregationStrategyMax = "max" + _ AttributeEnumAttr = iota AttributeEnumAttrRed AttributeEnumAttrGreen AttributeEnumAttrBlue @@ -99,13 +103,16 @@ func WithOptionalStringAttrMetricAttribute(optionalStringAttrAttributeValue stri } type metricDefaultMetric struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []int64 // slice containing number of aggregated datapoints at each index } // init fills default.metric metric with initial data. func (m *metricDefaultMetric) init() { + m.aggDataPoints = nil m.data.SetName("default.metric") m.data.SetDescription("Monotonic cumulative sum int metric enabled by default.") m.data.SetUnit("s") @@ -119,18 +126,56 @@ func (m *metricDefaultMetric) recordDataPoint(start pcommon.Timestamp, ts pcommo if !m.config.Enabled { return } - dp := m.data.Sum().DataPoints().AppendEmpty() + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) + + if m.attributes.StringAttr.Enabled { + dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) + } + if m.attributes.OverriddenIntAttr.Enabled { + dp.Attributes().PutInt("state", overriddenIntAttrAttributeValue) + } + if m.attributes.EnumAttr.Enabled { + dp.Attributes().PutStr("enum_attr", enumAttrAttributeValue) + } + if m.attributes.SliceAttr.Enabled { + dp.Attributes().PutEmptySlice("slice_attr").FromRaw(sliceAttrAttributeValue) + } + if m.attributes.MapAttr.Enabled { + dp.Attributes().PutEmptyMap("map_attr").FromRaw(mapAttrAttributeValue) + } + + var s string + dps := m.data.Sum().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.SetIntValue(dpi.IntValue() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.IntValue() > val { + dpi.SetIntValue(val) + } + return + case AggregationStrategyMax: + if dpi.IntValue() < val { + dpi.SetIntValue(val) + } + return + } + } + } + dp.SetIntValue(val) - dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) - dp.Attributes().PutInt("state", overriddenIntAttrAttributeValue) - dp.Attributes().PutStr("enum_attr", enumAttrAttributeValue) - dp.Attributes().PutEmptySlice("slice_attr").FromRaw(sliceAttrAttributeValue) - dp.Attributes().PutEmptyMap("map_attr").FromRaw(mapAttrAttributeValue) for _, op := range options { op.apply(dp) } + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -143,14 +188,19 @@ func (m *metricDefaultMetric) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metricDefaultMetric) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.Sum().DataPoints().At(i).SetIntValue(m.data.Sum().DataPoints().At(i).IntValue() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetricDefaultMetric(cfg MetricConfig) metricDefaultMetric { - m := metricDefaultMetric{config: cfg} +func newMetricDefaultMetric(cfg MetricConfig, acfg AttributesConfig) metricDefaultMetric { + m := metricDefaultMetric{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -159,13 +209,16 @@ func newMetricDefaultMetric(cfg MetricConfig) metricDefaultMetric { } type metricDefaultMetricToBeRemoved struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []float64 // slice containing number of aggregated datapoints at each index } // init fills default.metric.to_be_removed metric with initial data. func (m *metricDefaultMetricToBeRemoved) init() { + m.aggDataPoints = nil m.data.SetName("default.metric.to_be_removed") m.data.SetDescription("[DEPRECATED] Non-monotonic delta sum double metric enabled by default.") m.data.SetUnit("s") @@ -178,10 +231,37 @@ func (m *metricDefaultMetricToBeRemoved) recordDataPoint(start pcommon.Timestamp if !m.config.Enabled { return } - dp := m.data.Sum().DataPoints().AppendEmpty() + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) + + var s string + dps := m.data.Sum().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.SetDoubleValue(dpi.DoubleValue() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.DoubleValue() > val { + dpi.SetDoubleValue(val) + } + return + case AggregationStrategyMax: + if dpi.DoubleValue() < val { + dpi.SetDoubleValue(val) + } + return + } + } + } + dp.SetDoubleValue(val) + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -194,14 +274,19 @@ func (m *metricDefaultMetricToBeRemoved) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metricDefaultMetricToBeRemoved) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.Sum().DataPoints().At(i).SetDoubleValue(m.data.Sum().DataPoints().At(i).DoubleValue() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetricDefaultMetricToBeRemoved(cfg MetricConfig) metricDefaultMetricToBeRemoved { - m := metricDefaultMetricToBeRemoved{config: cfg} +func newMetricDefaultMetricToBeRemoved(cfg MetricConfig, acfg AttributesConfig) metricDefaultMetricToBeRemoved { + m := metricDefaultMetricToBeRemoved{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -210,13 +295,16 @@ func newMetricDefaultMetricToBeRemoved(cfg MetricConfig) metricDefaultMetricToBe } type metricMetricInputType struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []int64 // slice containing number of aggregated datapoints at each index } // init fills metric.input_type metric with initial data. func (m *metricMetricInputType) init() { + m.aggDataPoints = nil m.data.SetName("metric.input_type") m.data.SetDescription("Monotonic cumulative sum int metric with string input_type enabled by default.") m.data.SetUnit("s") @@ -230,15 +318,53 @@ func (m *metricMetricInputType) recordDataPoint(start pcommon.Timestamp, ts pcom if !m.config.Enabled { return } - dp := m.data.Sum().DataPoints().AppendEmpty() + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) + + if m.attributes.StringAttr.Enabled { + dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) + } + if m.attributes.OverriddenIntAttr.Enabled { + dp.Attributes().PutInt("state", overriddenIntAttrAttributeValue) + } + if m.attributes.EnumAttr.Enabled { + dp.Attributes().PutStr("enum_attr", enumAttrAttributeValue) + } + if m.attributes.SliceAttr.Enabled { + dp.Attributes().PutEmptySlice("slice_attr").FromRaw(sliceAttrAttributeValue) + } + if m.attributes.MapAttr.Enabled { + dp.Attributes().PutEmptyMap("map_attr").FromRaw(mapAttrAttributeValue) + } + + var s string + dps := m.data.Sum().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.SetIntValue(dpi.IntValue() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.IntValue() > val { + dpi.SetIntValue(val) + } + return + case AggregationStrategyMax: + if dpi.IntValue() < val { + dpi.SetIntValue(val) + } + return + } + } + } + dp.SetIntValue(val) - dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) - dp.Attributes().PutInt("state", overriddenIntAttrAttributeValue) - dp.Attributes().PutStr("enum_attr", enumAttrAttributeValue) - dp.Attributes().PutEmptySlice("slice_attr").FromRaw(sliceAttrAttributeValue) - dp.Attributes().PutEmptyMap("map_attr").FromRaw(mapAttrAttributeValue) + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -251,14 +377,19 @@ func (m *metricMetricInputType) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metricMetricInputType) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.Sum().DataPoints().At(i).SetIntValue(m.data.Sum().DataPoints().At(i).IntValue() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetricMetricInputType(cfg MetricConfig) metricMetricInputType { - m := metricMetricInputType{config: cfg} +func newMetricMetricInputType(cfg MetricConfig, acfg AttributesConfig) metricMetricInputType { + m := metricMetricInputType{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -267,13 +398,16 @@ func newMetricMetricInputType(cfg MetricConfig) metricMetricInputType { } type metricOptionalMetric struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []float64 // slice containing number of aggregated datapoints at each index } // init fills optional.metric metric with initial data. func (m *metricOptionalMetric) init() { + m.aggDataPoints = nil m.data.SetName("optional.metric") m.data.SetDescription("[DEPRECATED] Gauge double metric disabled by default.") m.data.SetUnit("1") @@ -285,16 +419,50 @@ func (m *metricOptionalMetric) recordDataPoint(start pcommon.Timestamp, ts pcomm if !m.config.Enabled { return } - dp := m.data.Gauge().DataPoints().AppendEmpty() + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) + + if m.attributes.StringAttr.Enabled { + dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) + } + if m.attributes.BooleanAttr.Enabled { + dp.Attributes().PutBool("boolean_attr", booleanAttrAttributeValue) + } + if m.attributes.BooleanAttr2.Enabled { + dp.Attributes().PutBool("boolean_attr2", booleanAttr2AttributeValue) + } + + var s string + dps := m.data.Gauge().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.SetDoubleValue(dpi.DoubleValue() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.DoubleValue() > val { + dpi.SetDoubleValue(val) + } + return + case AggregationStrategyMax: + if dpi.DoubleValue() < val { + dpi.SetDoubleValue(val) + } + return + } + } + } + dp.SetDoubleValue(val) - dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) - dp.Attributes().PutBool("boolean_attr", booleanAttrAttributeValue) - dp.Attributes().PutBool("boolean_attr2", booleanAttr2AttributeValue) for _, op := range options { op.apply(dp) } + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -307,14 +475,19 @@ func (m *metricOptionalMetric) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metricOptionalMetric) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.Gauge().DataPoints().At(i).SetDoubleValue(m.data.Gauge().DataPoints().At(i).DoubleValue() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetricOptionalMetric(cfg MetricConfig) metricOptionalMetric { - m := metricOptionalMetric{config: cfg} +func newMetricOptionalMetric(cfg MetricConfig, acfg AttributesConfig) metricOptionalMetric { + m := metricOptionalMetric{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -323,13 +496,16 @@ func newMetricOptionalMetric(cfg MetricConfig) metricOptionalMetric { } type metricOptionalMetricEmptyUnit struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []float64 // slice containing number of aggregated datapoints at each index } // init fills optional.metric.empty_unit metric with initial data. func (m *metricOptionalMetricEmptyUnit) init() { + m.aggDataPoints = nil m.data.SetName("optional.metric.empty_unit") m.data.SetDescription("[DEPRECATED] Gauge double metric disabled by default.") m.data.SetUnit("") @@ -341,12 +517,44 @@ func (m *metricOptionalMetricEmptyUnit) recordDataPoint(start pcommon.Timestamp, if !m.config.Enabled { return } - dp := m.data.Gauge().DataPoints().AppendEmpty() + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) + + if m.attributes.StringAttr.Enabled { + dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) + } + if m.attributes.BooleanAttr.Enabled { + dp.Attributes().PutBool("boolean_attr", booleanAttrAttributeValue) + } + + var s string + dps := m.data.Gauge().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.SetDoubleValue(dpi.DoubleValue() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.DoubleValue() > val { + dpi.SetDoubleValue(val) + } + return + case AggregationStrategyMax: + if dpi.DoubleValue() < val { + dpi.SetDoubleValue(val) + } + return + } + } + } + dp.SetDoubleValue(val) - dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) - dp.Attributes().PutBool("boolean_attr", booleanAttrAttributeValue) + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -359,14 +567,19 @@ func (m *metricOptionalMetricEmptyUnit) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metricOptionalMetricEmptyUnit) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.Gauge().DataPoints().At(i).SetDoubleValue(m.data.Gauge().DataPoints().At(i).DoubleValue() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetricOptionalMetricEmptyUnit(cfg MetricConfig) metricOptionalMetricEmptyUnit { - m := metricOptionalMetricEmptyUnit{config: cfg} +func newMetricOptionalMetricEmptyUnit(cfg MetricConfig, acfg AttributesConfig) metricOptionalMetricEmptyUnit { + m := metricOptionalMetricEmptyUnit{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -435,11 +648,11 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.Settings, opt startTime: pcommon.NewTimestampFromTime(time.Now()), metricsBuffer: pmetric.NewMetrics(), buildInfo: settings.BuildInfo, - metricDefaultMetric: newMetricDefaultMetric(mbc.Metrics.DefaultMetric), - metricDefaultMetricToBeRemoved: newMetricDefaultMetricToBeRemoved(mbc.Metrics.DefaultMetricToBeRemoved), - metricMetricInputType: newMetricMetricInputType(mbc.Metrics.MetricInputType), - metricOptionalMetric: newMetricOptionalMetric(mbc.Metrics.OptionalMetric), - metricOptionalMetricEmptyUnit: newMetricOptionalMetricEmptyUnit(mbc.Metrics.OptionalMetricEmptyUnit), + metricDefaultMetric: newMetricDefaultMetric(mbc.Metrics.DefaultMetric, mbc.Attributes), + metricDefaultMetricToBeRemoved: newMetricDefaultMetricToBeRemoved(mbc.Metrics.DefaultMetricToBeRemoved, mbc.Attributes), + metricMetricInputType: newMetricMetricInputType(mbc.Metrics.MetricInputType, mbc.Attributes), + metricOptionalMetric: newMetricOptionalMetric(mbc.Metrics.OptionalMetric, mbc.Attributes), + metricOptionalMetricEmptyUnit: newMetricOptionalMetricEmptyUnit(mbc.Metrics.OptionalMetricEmptyUnit, mbc.Attributes), resourceAttributeIncludeFilter: make(map[string]filter.Filter), resourceAttributeExcludeFilter: make(map[string]filter.Filter), } @@ -600,11 +813,13 @@ func (mb *MetricsBuilder) Emit(options ...ResourceMetricsOption) pmetric.Metrics // RecordDefaultMetricDataPoint adds a data point to default.metric metric. func (mb *MetricsBuilder) RecordDefaultMetricDataPoint(ts pcommon.Timestamp, val int64, stringAttrAttributeValue string, overriddenIntAttrAttributeValue int64, enumAttrAttributeValue AttributeEnumAttr, sliceAttrAttributeValue []any, mapAttrAttributeValue map[string]any, options ...MetricAttributeOption) { mb.metricDefaultMetric.recordDataPoint(mb.startTime, ts, val, stringAttrAttributeValue, overriddenIntAttrAttributeValue, enumAttrAttributeValue.String(), sliceAttrAttributeValue, mapAttrAttributeValue, options...) + return } // RecordDefaultMetricToBeRemovedDataPoint adds a data point to default.metric.to_be_removed metric. func (mb *MetricsBuilder) RecordDefaultMetricToBeRemovedDataPoint(ts pcommon.Timestamp, val float64) { mb.metricDefaultMetricToBeRemoved.recordDataPoint(mb.startTime, ts, val) + return } // RecordMetricInputTypeDataPoint adds a data point to metric.input_type metric. @@ -620,11 +835,13 @@ func (mb *MetricsBuilder) RecordMetricInputTypeDataPoint(ts pcommon.Timestamp, i // RecordOptionalMetricDataPoint adds a data point to optional.metric metric. func (mb *MetricsBuilder) RecordOptionalMetricDataPoint(ts pcommon.Timestamp, val float64, stringAttrAttributeValue string, booleanAttrAttributeValue bool, booleanAttr2AttributeValue bool, options ...MetricAttributeOption) { mb.metricOptionalMetric.recordDataPoint(mb.startTime, ts, val, stringAttrAttributeValue, booleanAttrAttributeValue, booleanAttr2AttributeValue, options...) + return } // RecordOptionalMetricEmptyUnitDataPoint adds a data point to optional.metric.empty_unit metric. func (mb *MetricsBuilder) RecordOptionalMetricEmptyUnitDataPoint(ts pcommon.Timestamp, val float64, stringAttrAttributeValue string, booleanAttrAttributeValue bool) { mb.metricOptionalMetricEmptyUnit.recordDataPoint(mb.startTime, ts, val, stringAttrAttributeValue, booleanAttrAttributeValue) + return } // Reset resets metrics builder to its initial state. It should be used when external metrics source is restarted, diff --git a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics_test.go b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics_test.go index e11ac2a2e84..a4c08860686 100644 --- a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics_test.go +++ b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics_test.go @@ -100,6 +100,8 @@ func TestMetricsBuilder(t *testing.T) { defaultMetricsCount++ allMetricsCount++ mb.RecordDefaultMetricDataPoint(ts, 1, "string_attr-val", 19, AttributeEnumAttrRed, []any{"slice_attr-item1", "slice_attr-item2"}, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}, WithOptionalIntAttrMetricAttribute(17), WithOptionalStringAttrMetricAttribute("optional_string_attr-val")) + allMetricsCount++ + mb.RecordDefaultMetricDataPoint(ts, 3, "string_attr-val-2", 19, AttributeEnumAttrRed, []any{"slice_attr-item1", "slice_attr-item2"}, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}, WithOptionalIntAttrMetricAttribute(17), WithOptionalStringAttrMetricAttribute("optional_string_attr-val")) defaultMetricsCount++ allMetricsCount++ @@ -108,12 +110,18 @@ func TestMetricsBuilder(t *testing.T) { defaultMetricsCount++ allMetricsCount++ mb.RecordMetricInputTypeDataPoint(ts, "1", "string_attr-val", 19, AttributeEnumAttrRed, []any{"slice_attr-item1", "slice_attr-item2"}, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}) + allMetricsCount++ + mb.RecordMetricInputTypeDataPoint(ts, "3", "string_attr-val-2", 19, AttributeEnumAttrRed, []any{"slice_attr-item1", "slice_attr-item2"}, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}) allMetricsCount++ mb.RecordOptionalMetricDataPoint(ts, 1, "string_attr-val", true, false, WithOptionalStringAttrMetricAttribute("optional_string_attr-val")) + allMetricsCount++ + mb.RecordOptionalMetricDataPoint(ts, 3, "string_attr-val-2", true, false, WithOptionalStringAttrMetricAttribute("optional_string_attr-val")) allMetricsCount++ mb.RecordOptionalMetricEmptyUnitDataPoint(ts, 1, "string_attr-val", true) + allMetricsCount++ + mb.RecordOptionalMetricEmptyUnitDataPoint(ts, 3, "string_attr-val-2", true) rb := mb.NewResourceBuilder() rb.SetMapResourceAttr(map[string]any{"key1": "map.resource.attr-val1", "key2": "map.resource.attr-val2"}) @@ -141,7 +149,7 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, defaultMetricsCount, ms.Len()) } if tt.metricsSet == testDataSetAll { - assert.Equal(t, allMetricsCount, ms.Len()) + assert.Equal(t, (allMetricsCount/2)+1, ms.Len()) } validatedMetrics := make(map[string]bool) for i := 0; i < ms.Len(); i++ { @@ -150,122 +158,214 @@ func TestMetricsBuilder(t *testing.T) { assert.False(t, validatedMetrics["default.metric"], "Found a duplicate in the metrics slice: default.metric") validatedMetrics["default.metric"] = true assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) - assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + dp := ms.At(i).Sum().DataPoints().At(0) + if tt.name == "all_set" { + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + } else { + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(4), dp.IntValue()) + } assert.Equal(t, "Monotonic cumulative sum int metric enabled by default.", ms.At(i).Description()) assert.Equal(t, "s", ms.At(i).Unit()) assert.True(t, ms.At(i).Sum().IsMonotonic()) assert.Equal(t, pmetric.AggregationTemporalityCumulative, ms.At(i).Sum().AggregationTemporality()) - dp := ms.At(i).Sum().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) - assert.Equal(t, int64(1), dp.IntValue()) attrVal, ok := dp.Attributes().Get("string_attr") - assert.True(t, ok) - assert.Equal(t, "string_attr-val", attrVal.Str()) + if !mb.config.Attributes.StringAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "string_attr-val", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("state") - assert.True(t, ok) - assert.EqualValues(t, 19, attrVal.Int()) + if !mb.config.Attributes.OverriddenIntAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.EqualValues(t, 19, attrVal.Int()) + } attrVal, ok = dp.Attributes().Get("enum_attr") - assert.True(t, ok) - assert.Equal(t, "red", attrVal.Str()) + if !mb.config.Attributes.EnumAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "red", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("slice_attr") - assert.True(t, ok) - assert.Equal(t, []any{"slice_attr-item1", "slice_attr-item2"}, attrVal.Slice().AsRaw()) + if !mb.config.Attributes.SliceAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, []any{"slice_attr-item1", "slice_attr-item2"}, attrVal.Slice().AsRaw()) + } attrVal, ok = dp.Attributes().Get("map_attr") - assert.True(t, ok) - assert.Equal(t, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}, attrVal.Map().AsRaw()) + if !mb.config.Attributes.MapAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}, attrVal.Map().AsRaw()) + } attrVal, ok = dp.Attributes().Get("optional_int_attr") - assert.True(t, ok) - assert.EqualValues(t, 17, attrVal.Int()) + if !mb.config.Attributes.OptionalIntAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.EqualValues(t, 17, attrVal.Int()) + } attrVal, ok = dp.Attributes().Get("optional_string_attr") - assert.True(t, ok) - assert.Equal(t, "optional_string_attr-val", attrVal.Str()) + if !mb.config.Attributes.OptionalStringAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "optional_string_attr-val", attrVal.Str()) + } case "default.metric.to_be_removed": assert.False(t, validatedMetrics["default.metric.to_be_removed"], "Found a duplicate in the metrics slice: default.metric.to_be_removed") validatedMetrics["default.metric.to_be_removed"] = true assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + dp := ms.At(i).Sum().DataPoints().At(0) assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) assert.Equal(t, "[DEPRECATED] Non-monotonic delta sum double metric enabled by default.", ms.At(i).Description()) assert.Equal(t, "s", ms.At(i).Unit()) assert.False(t, ms.At(i).Sum().IsMonotonic()) assert.Equal(t, pmetric.AggregationTemporalityDelta, ms.At(i).Sum().AggregationTemporality()) - dp := ms.At(i).Sum().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) - assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) case "metric.input_type": assert.False(t, validatedMetrics["metric.input_type"], "Found a duplicate in the metrics slice: metric.input_type") validatedMetrics["metric.input_type"] = true assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) - assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + dp := ms.At(i).Sum().DataPoints().At(0) + if tt.name == "all_set" { + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + } else { + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(4), dp.IntValue()) + } assert.Equal(t, "Monotonic cumulative sum int metric with string input_type enabled by default.", ms.At(i).Description()) assert.Equal(t, "s", ms.At(i).Unit()) assert.True(t, ms.At(i).Sum().IsMonotonic()) assert.Equal(t, pmetric.AggregationTemporalityCumulative, ms.At(i).Sum().AggregationTemporality()) - dp := ms.At(i).Sum().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) - assert.Equal(t, int64(1), dp.IntValue()) attrVal, ok := dp.Attributes().Get("string_attr") - assert.True(t, ok) - assert.Equal(t, "string_attr-val", attrVal.Str()) + if !mb.config.Attributes.StringAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "string_attr-val", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("state") - assert.True(t, ok) - assert.EqualValues(t, 19, attrVal.Int()) + if !mb.config.Attributes.OverriddenIntAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.EqualValues(t, 19, attrVal.Int()) + } attrVal, ok = dp.Attributes().Get("enum_attr") - assert.True(t, ok) - assert.Equal(t, "red", attrVal.Str()) + if !mb.config.Attributes.EnumAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "red", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("slice_attr") - assert.True(t, ok) - assert.Equal(t, []any{"slice_attr-item1", "slice_attr-item2"}, attrVal.Slice().AsRaw()) + if !mb.config.Attributes.SliceAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, []any{"slice_attr-item1", "slice_attr-item2"}, attrVal.Slice().AsRaw()) + } attrVal, ok = dp.Attributes().Get("map_attr") - assert.True(t, ok) - assert.Equal(t, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}, attrVal.Map().AsRaw()) + if !mb.config.Attributes.MapAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}, attrVal.Map().AsRaw()) + } case "optional.metric": assert.False(t, validatedMetrics["optional.metric"], "Found a duplicate in the metrics slice: optional.metric") validatedMetrics["optional.metric"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) - assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + dp := ms.At(i).Gauge().DataPoints().At(0) + if tt.name == "all_set" { + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + } else { + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(4), dp.DoubleValue(), 0.01) + } assert.Equal(t, "[DEPRECATED] Gauge double metric disabled by default.", ms.At(i).Description()) assert.Equal(t, "1", ms.At(i).Unit()) - dp := ms.At(i).Gauge().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) - assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) attrVal, ok := dp.Attributes().Get("string_attr") - assert.True(t, ok) - assert.Equal(t, "string_attr-val", attrVal.Str()) + if !mb.config.Attributes.StringAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "string_attr-val", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("boolean_attr") - assert.True(t, ok) - assert.True(t, attrVal.Bool()) + if !mb.config.Attributes.BooleanAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.True(t, attrVal.Bool()) + } attrVal, ok = dp.Attributes().Get("boolean_attr2") - assert.True(t, ok) - assert.False(t, attrVal.Bool()) + if !mb.config.Attributes.BooleanAttr2.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.False(t, attrVal.Bool()) + } attrVal, ok = dp.Attributes().Get("optional_string_attr") - assert.True(t, ok) - assert.Equal(t, "optional_string_attr-val", attrVal.Str()) + if !mb.config.Attributes.OptionalStringAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "optional_string_attr-val", attrVal.Str()) + } case "optional.metric.empty_unit": assert.False(t, validatedMetrics["optional.metric.empty_unit"], "Found a duplicate in the metrics slice: optional.metric.empty_unit") validatedMetrics["optional.metric.empty_unit"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) - assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + dp := ms.At(i).Gauge().DataPoints().At(0) + if tt.name == "all_set" { + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + } else { + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(4), dp.DoubleValue(), 0.01) + } assert.Equal(t, "[DEPRECATED] Gauge double metric disabled by default.", ms.At(i).Description()) assert.Empty(t, ms.At(i).Unit()) - dp := ms.At(i).Gauge().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) - assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) attrVal, ok := dp.Attributes().Get("string_attr") - assert.True(t, ok) - assert.Equal(t, "string_attr-val", attrVal.Str()) + if !mb.config.Attributes.StringAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "string_attr-val", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("boolean_attr") - assert.True(t, ok) - assert.True(t, attrVal.Bool()) + if !mb.config.Attributes.BooleanAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.True(t, attrVal.Bool()) + } } } }) diff --git a/cmd/mdatagen/internal/samplereceiver/internal/metadata/testdata/config.yaml b/cmd/mdatagen/internal/samplereceiver/internal/metadata/testdata/config.yaml index b8e4438bb0d..2ae6304751e 100644 --- a/cmd/mdatagen/internal/samplereceiver/internal/metadata/testdata/config.yaml +++ b/cmd/mdatagen/internal/samplereceiver/internal/metadata/testdata/config.yaml @@ -1,5 +1,24 @@ default: all_set: + attributes: + boolean_attr: + enabled: true + boolean_attr2: + enabled: true + enum_attr: + enabled: true + map_attr: + enabled: true + optional_int_attr: + enabled: true + optional_string_attr: + enabled: true + overridden_int_attr: + enabled: true + slice_attr: + enabled: true + string_attr: + enabled: true metrics: default.metric: enabled: true @@ -36,6 +55,25 @@ all_set: string.resource.attr_to_be_removed: enabled: true none_set: + attributes: + boolean_attr: + enabled: false + boolean_attr2: + enabled: false + enum_attr: + enabled: false + map_attr: + enabled: false + optional_int_attr: + enabled: false + optional_string_attr: + enabled: false + overridden_int_attr: + enabled: false + slice_attr: + enabled: false + string_attr: + enabled: false metrics: default.metric: enabled: false diff --git a/cmd/mdatagen/internal/samplereceiver/metadata.yaml b/cmd/mdatagen/internal/samplereceiver/metadata.yaml index 95b22c30741..7837f893b19 100644 --- a/cmd/mdatagen/internal/samplereceiver/metadata.yaml +++ b/cmd/mdatagen/internal/samplereceiver/metadata.yaml @@ -77,44 +77,53 @@ attributes: string_attr: description: Attribute with any string value. type: string + enabled: false overridden_int_attr: name_override: state description: Integer attribute with overridden name. type: int + enabled: true enum_attr: description: Attribute with a known set of string values. type: string enum: [red, green, blue] + enabled: true boolean_attr: description: Attribute with a boolean value. type: bool + enabled: true # This 2nd boolean attribute allows us to test both values for boolean attributes, # as test values are based on the parity of the attribute name length. boolean_attr2: description: Another attribute with a boolean value. type: bool + enabled: true slice_attr: description: Attribute with a slice value. type: slice + enabled: true map_attr: description: Attribute with a map value. type: map + enabled: true optional_int_attr: description: An optional attribute with an integer value type: int optional: true + enabled: true optional_string_attr: description: An optional attribute with any string value type: string optional: true + enabled: true events: default.event: diff --git a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config.go b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config.go index 05ddc47ac40..89dbc5aa70e 100644 --- a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config.go +++ b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config.go @@ -3,13 +3,16 @@ package metadata import ( + "fmt" + "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/filter" ) // MetricConfig provides common config for a particular metric. type MetricConfig struct { - Enabled bool `mapstructure:"enabled"` + Enabled bool `mapstructure:"enabled"` + AggregationStrategy string `mapstructure:"aggregation_strategy"` enabledSetByUser bool } @@ -22,10 +25,23 @@ func (ms *MetricConfig) Unmarshal(parser *confmap.Conf) error { if err != nil { return err } + + if ms.AggregationStrategy != AggregationStrategySum && + ms.AggregationStrategy != AggregationStrategyAvg && + ms.AggregationStrategy != AggregationStrategyMin && + ms.AggregationStrategy != AggregationStrategyMax { + return fmt.Errorf("invalid aggregation strategy set: '%v'", ms.AggregationStrategy) + } + ms.enabledSetByUser = parser.IsSet("enabled") return nil } +// AttributeConfig holds configuration information for a particular metric. +type AttributeConfig struct { + Enabled bool `mapstructure:"enabled"` +} + // MetricsConfig provides config for sample metrics. type MetricsConfig struct { DefaultMetric MetricConfig `mapstructure:"default.metric"` @@ -35,22 +51,65 @@ type MetricsConfig struct { OptionalMetricEmptyUnit MetricConfig `mapstructure:"optional.metric.empty_unit"` } +// AttributesConfig is the collected configuration for all attributes in the +// component +type AttributesConfig struct { + BooleanAttr AttributeConfig `mapstructure:"boolean_attr"` + BooleanAttr2 AttributeConfig `mapstructure:"boolean_attr2"` + EnumAttr AttributeConfig `mapstructure:"enum_attr"` + MapAttr AttributeConfig `mapstructure:"map_attr"` + OverriddenIntAttr AttributeConfig `mapstructure:"overridden_int_attr"` + SliceAttr AttributeConfig `mapstructure:"slice_attr"` + StringAttr AttributeConfig `mapstructure:"string_attr"` +} + +func DefaultAttributesConfig() AttributesConfig { + return AttributesConfig{ + BooleanAttr: AttributeConfig{ + Enabled: true, + }, + BooleanAttr2: AttributeConfig{ + Enabled: true, + }, + EnumAttr: AttributeConfig{ + Enabled: true, + }, + MapAttr: AttributeConfig{ + Enabled: true, + }, + OverriddenIntAttr: AttributeConfig{ + Enabled: true, + }, + SliceAttr: AttributeConfig{ + Enabled: true, + }, + StringAttr: AttributeConfig{ + Enabled: false, + }, + } +} + func DefaultMetricsConfig() MetricsConfig { return MetricsConfig{ DefaultMetric: MetricConfig{ - Enabled: true, + Enabled: true, + AggregationStrategy: AggregationStrategySum, }, DefaultMetricToBeRemoved: MetricConfig{ - Enabled: true, + Enabled: true, + AggregationStrategy: AggregationStrategySum, }, MetricInputType: MetricConfig{ - Enabled: true, + Enabled: true, + AggregationStrategy: AggregationStrategySum, }, OptionalMetric: MetricConfig{ - Enabled: false, + Enabled: false, + AggregationStrategy: AggregationStrategyAvg, }, OptionalMetricEmptyUnit: MetricConfig{ - Enabled: false, + Enabled: false, + AggregationStrategy: AggregationStrategyAvg, }, } } @@ -125,12 +184,14 @@ func DefaultResourceAttributesConfig() ResourceAttributesConfig { // MetricsBuilderConfig is a configuration for sample metrics builder. type MetricsBuilderConfig struct { Metrics MetricsConfig `mapstructure:"metrics"` + Attributes AttributesConfig `mapstructure:"attributes"` ResourceAttributes ResourceAttributesConfig `mapstructure:"resource_attributes"` } func DefaultMetricsBuilderConfig() MetricsBuilderConfig { return MetricsBuilderConfig{ Metrics: DefaultMetricsConfig(), + Attributes: DefaultAttributesConfig(), ResourceAttributes: DefaultResourceAttributesConfig(), } } diff --git a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config_test.go b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config_test.go index cc1a2cd0525..94bd127bccd 100644 --- a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config_test.go +++ b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config_test.go @@ -27,11 +27,42 @@ func TestMetricsBuilderConfig(t *testing.T) { name: "all_set", want: MetricsBuilderConfig{ Metrics: MetricsConfig{ - DefaultMetric: MetricConfig{Enabled: true}, - DefaultMetricToBeRemoved: MetricConfig{Enabled: true}, - MetricInputType: MetricConfig{Enabled: true}, - OptionalMetric: MetricConfig{Enabled: true}, - OptionalMetricEmptyUnit: MetricConfig{Enabled: true}, + DefaultMetric: MetricConfig{ + Enabled: true, + AggregationStrategy: "sum"}, + DefaultMetricToBeRemoved: MetricConfig{ + Enabled: true, + AggregationStrategy: "sum"}, + MetricInputType: MetricConfig{ + Enabled: true, + AggregationStrategy: "sum"}, + OptionalMetric: MetricConfig{ + Enabled: true, AggregationStrategy: "avg"}, + OptionalMetricEmptyUnit: MetricConfig{ + Enabled: true, AggregationStrategy: "avg"}, + }, + Attributes: AttributesConfig{ + BooleanAttr: AttributeConfig{ + Enabled: true, + }, + BooleanAttr2: AttributeConfig{ + Enabled: true, + }, + EnumAttr: AttributeConfig{ + Enabled: true, + }, + MapAttr: AttributeConfig{ + Enabled: true, + }, + OverriddenIntAttr: AttributeConfig{ + Enabled: true, + }, + SliceAttr: AttributeConfig{ + Enabled: true, + }, + StringAttr: AttributeConfig{ + Enabled: true, + }, }, ResourceAttributes: ResourceAttributesConfig{ MapResourceAttr: ResourceAttributeConfig{Enabled: true}, @@ -49,11 +80,42 @@ func TestMetricsBuilderConfig(t *testing.T) { name: "none_set", want: MetricsBuilderConfig{ Metrics: MetricsConfig{ - DefaultMetric: MetricConfig{Enabled: false}, - DefaultMetricToBeRemoved: MetricConfig{Enabled: false}, - MetricInputType: MetricConfig{Enabled: false}, - OptionalMetric: MetricConfig{Enabled: false}, - OptionalMetricEmptyUnit: MetricConfig{Enabled: false}, + DefaultMetric: MetricConfig{ + Enabled: false, + AggregationStrategy: "sum"}, + DefaultMetricToBeRemoved: MetricConfig{ + Enabled: false, + AggregationStrategy: "sum"}, + MetricInputType: MetricConfig{ + Enabled: false, + AggregationStrategy: "sum"}, + OptionalMetric: MetricConfig{ + Enabled: false, AggregationStrategy: "avg"}, + OptionalMetricEmptyUnit: MetricConfig{ + Enabled: false, AggregationStrategy: "avg"}, + }, + Attributes: AttributesConfig{ + BooleanAttr: AttributeConfig{ + Enabled: false, + }, + BooleanAttr2: AttributeConfig{ + Enabled: false, + }, + EnumAttr: AttributeConfig{ + Enabled: false, + }, + MapAttr: AttributeConfig{ + Enabled: false, + }, + OverriddenIntAttr: AttributeConfig{ + Enabled: false, + }, + SliceAttr: AttributeConfig{ + Enabled: false, + }, + StringAttr: AttributeConfig{ + Enabled: false, + }, }, ResourceAttributes: ResourceAttributesConfig{ MapResourceAttr: ResourceAttributeConfig{Enabled: false}, @@ -87,6 +149,61 @@ func loadMetricsBuilderConfig(t *testing.T, name string) MetricsBuilderConfig { return cfg } +func TestAttributesConfig(t *testing.T) { + tests := []struct { + name string + want AttributesConfig + }{ + { + name: "default", + want: DefaultAttributesConfig(), + }, + { + name: "all_set", + want: AttributesConfig{ + BooleanAttr: AttributeConfig{Enabled: true}, + BooleanAttr2: AttributeConfig{Enabled: true}, + EnumAttr: AttributeConfig{Enabled: true}, + MapAttr: AttributeConfig{Enabled: true}, + OverriddenIntAttr: AttributeConfig{Enabled: true}, + SliceAttr: AttributeConfig{Enabled: true}, + StringAttr: AttributeConfig{Enabled: true}, + }, + }, + { + name: "none_set", + want: AttributesConfig{ + BooleanAttr: AttributeConfig{Enabled: false}, + BooleanAttr2: AttributeConfig{Enabled: false}, + EnumAttr: AttributeConfig{Enabled: false}, + MapAttr: AttributeConfig{Enabled: false}, + OverriddenIntAttr: AttributeConfig{Enabled: false}, + SliceAttr: AttributeConfig{Enabled: false}, + StringAttr: AttributeConfig{Enabled: false}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := loadAttributesConfig(t, tt.name) + diff := cmp.Diff(tt.want, cfg, cmpopts.IgnoreUnexported(AttributeConfig{})) + require.Emptyf(t, diff, "Config mismatch (-expected +actual):\n%s", diff) + }) + } +} + +func loadAttributesConfig(t *testing.T, name string) AttributesConfig { + cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) + require.NoError(t, err) + sub, err := cm.Sub(name) + require.NoError(t, err) + sub, err = sub.Sub("attributes") + require.NoError(t, err) + cfg := DefaultAttributesConfig() + require.NoError(t, sub.Unmarshal(&cfg)) + return cfg +} + func TestResourceAttributesConfig(t *testing.T) { tests := []struct { name string diff --git a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics.go b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics.go index af8e1308cc8..57434a13920 100644 --- a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics.go +++ b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics.go @@ -20,7 +20,11 @@ import ( type AttributeEnumAttr int const ( - _ AttributeEnumAttr = iota + AggregationStrategySum = "sum" + AggregationStrategyAvg = "avg" + AggregationStrategyMin = "min" + AggregationStrategyMax = "max" + _ AttributeEnumAttr = iota AttributeEnumAttrRed AttributeEnumAttrGreen AttributeEnumAttrBlue @@ -77,13 +81,16 @@ type metricInfo struct { } type metricDefaultMetric struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []int64 // slice containing number of aggregated datapoints at each index } // init fills default.metric metric with initial data. func (m *metricDefaultMetric) init() { + m.aggDataPoints = nil m.data.SetName("default.metric") m.data.SetDescription("Monotonic cumulative sum int metric enabled by default.") m.data.SetUnit("s") @@ -97,15 +104,53 @@ func (m *metricDefaultMetric) recordDataPoint(start pcommon.Timestamp, ts pcommo if !m.config.Enabled { return } - dp := m.data.Sum().DataPoints().AppendEmpty() + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) + + if m.attributes.StringAttr.Enabled { + dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) + } + if m.attributes.OverriddenIntAttr.Enabled { + dp.Attributes().PutInt("state", overriddenIntAttrAttributeValue) + } + if m.attributes.EnumAttr.Enabled { + dp.Attributes().PutStr("enum_attr", enumAttrAttributeValue) + } + if m.attributes.SliceAttr.Enabled { + dp.Attributes().PutEmptySlice("slice_attr").FromRaw(sliceAttrAttributeValue) + } + if m.attributes.MapAttr.Enabled { + dp.Attributes().PutEmptyMap("map_attr").FromRaw(mapAttrAttributeValue) + } + + var s string + dps := m.data.Sum().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.SetIntValue(dpi.IntValue() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.IntValue() > val { + dpi.SetIntValue(val) + } + return + case AggregationStrategyMax: + if dpi.IntValue() < val { + dpi.SetIntValue(val) + } + return + } + } + } + dp.SetIntValue(val) - dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) - dp.Attributes().PutInt("state", overriddenIntAttrAttributeValue) - dp.Attributes().PutStr("enum_attr", enumAttrAttributeValue) - dp.Attributes().PutEmptySlice("slice_attr").FromRaw(sliceAttrAttributeValue) - dp.Attributes().PutEmptyMap("map_attr").FromRaw(mapAttrAttributeValue) + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -118,14 +163,19 @@ func (m *metricDefaultMetric) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metricDefaultMetric) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.Sum().DataPoints().At(i).SetIntValue(m.data.Sum().DataPoints().At(i).IntValue() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetricDefaultMetric(cfg MetricConfig) metricDefaultMetric { - m := metricDefaultMetric{config: cfg} +func newMetricDefaultMetric(cfg MetricConfig, acfg AttributesConfig) metricDefaultMetric { + m := metricDefaultMetric{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -134,13 +184,16 @@ func newMetricDefaultMetric(cfg MetricConfig) metricDefaultMetric { } type metricDefaultMetricToBeRemoved struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []float64 // slice containing number of aggregated datapoints at each index } // init fills default.metric.to_be_removed metric with initial data. func (m *metricDefaultMetricToBeRemoved) init() { + m.aggDataPoints = nil m.data.SetName("default.metric.to_be_removed") m.data.SetDescription("[DEPRECATED] Non-monotonic delta sum double metric enabled by default.") m.data.SetUnit("s") @@ -153,10 +206,37 @@ func (m *metricDefaultMetricToBeRemoved) recordDataPoint(start pcommon.Timestamp if !m.config.Enabled { return } - dp := m.data.Sum().DataPoints().AppendEmpty() + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) + + var s string + dps := m.data.Sum().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.SetDoubleValue(dpi.DoubleValue() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.DoubleValue() > val { + dpi.SetDoubleValue(val) + } + return + case AggregationStrategyMax: + if dpi.DoubleValue() < val { + dpi.SetDoubleValue(val) + } + return + } + } + } + dp.SetDoubleValue(val) + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -169,14 +249,19 @@ func (m *metricDefaultMetricToBeRemoved) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metricDefaultMetricToBeRemoved) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.Sum().DataPoints().At(i).SetDoubleValue(m.data.Sum().DataPoints().At(i).DoubleValue() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetricDefaultMetricToBeRemoved(cfg MetricConfig) metricDefaultMetricToBeRemoved { - m := metricDefaultMetricToBeRemoved{config: cfg} +func newMetricDefaultMetricToBeRemoved(cfg MetricConfig, acfg AttributesConfig) metricDefaultMetricToBeRemoved { + m := metricDefaultMetricToBeRemoved{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -185,13 +270,16 @@ func newMetricDefaultMetricToBeRemoved(cfg MetricConfig) metricDefaultMetricToBe } type metricMetricInputType struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []int64 // slice containing number of aggregated datapoints at each index } // init fills metric.input_type metric with initial data. func (m *metricMetricInputType) init() { + m.aggDataPoints = nil m.data.SetName("metric.input_type") m.data.SetDescription("Monotonic cumulative sum int metric with string input_type enabled by default.") m.data.SetUnit("s") @@ -205,15 +293,53 @@ func (m *metricMetricInputType) recordDataPoint(start pcommon.Timestamp, ts pcom if !m.config.Enabled { return } - dp := m.data.Sum().DataPoints().AppendEmpty() + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) + + if m.attributes.StringAttr.Enabled { + dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) + } + if m.attributes.OverriddenIntAttr.Enabled { + dp.Attributes().PutInt("state", overriddenIntAttrAttributeValue) + } + if m.attributes.EnumAttr.Enabled { + dp.Attributes().PutStr("enum_attr", enumAttrAttributeValue) + } + if m.attributes.SliceAttr.Enabled { + dp.Attributes().PutEmptySlice("slice_attr").FromRaw(sliceAttrAttributeValue) + } + if m.attributes.MapAttr.Enabled { + dp.Attributes().PutEmptyMap("map_attr").FromRaw(mapAttrAttributeValue) + } + + var s string + dps := m.data.Sum().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.SetIntValue(dpi.IntValue() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.IntValue() > val { + dpi.SetIntValue(val) + } + return + case AggregationStrategyMax: + if dpi.IntValue() < val { + dpi.SetIntValue(val) + } + return + } + } + } + dp.SetIntValue(val) - dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) - dp.Attributes().PutInt("state", overriddenIntAttrAttributeValue) - dp.Attributes().PutStr("enum_attr", enumAttrAttributeValue) - dp.Attributes().PutEmptySlice("slice_attr").FromRaw(sliceAttrAttributeValue) - dp.Attributes().PutEmptyMap("map_attr").FromRaw(mapAttrAttributeValue) + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -226,14 +352,19 @@ func (m *metricMetricInputType) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metricMetricInputType) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.Sum().DataPoints().At(i).SetIntValue(m.data.Sum().DataPoints().At(i).IntValue() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetricMetricInputType(cfg MetricConfig) metricMetricInputType { - m := metricMetricInputType{config: cfg} +func newMetricMetricInputType(cfg MetricConfig, acfg AttributesConfig) metricMetricInputType { + m := metricMetricInputType{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -242,13 +373,16 @@ func newMetricMetricInputType(cfg MetricConfig) metricMetricInputType { } type metricOptionalMetric struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []float64 // slice containing number of aggregated datapoints at each index } // init fills optional.metric metric with initial data. func (m *metricOptionalMetric) init() { + m.aggDataPoints = nil m.data.SetName("optional.metric") m.data.SetDescription("[DEPRECATED] Gauge double metric disabled by default.") m.data.SetUnit("1") @@ -260,13 +394,47 @@ func (m *metricOptionalMetric) recordDataPoint(start pcommon.Timestamp, ts pcomm if !m.config.Enabled { return } - dp := m.data.Gauge().DataPoints().AppendEmpty() + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) + + if m.attributes.StringAttr.Enabled { + dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) + } + if m.attributes.BooleanAttr.Enabled { + dp.Attributes().PutBool("boolean_attr", booleanAttrAttributeValue) + } + if m.attributes.BooleanAttr2.Enabled { + dp.Attributes().PutBool("boolean_attr2", booleanAttr2AttributeValue) + } + + var s string + dps := m.data.Gauge().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.SetDoubleValue(dpi.DoubleValue() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.DoubleValue() > val { + dpi.SetDoubleValue(val) + } + return + case AggregationStrategyMax: + if dpi.DoubleValue() < val { + dpi.SetDoubleValue(val) + } + return + } + } + } + dp.SetDoubleValue(val) - dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) - dp.Attributes().PutBool("boolean_attr", booleanAttrAttributeValue) - dp.Attributes().PutBool("boolean_attr2", booleanAttr2AttributeValue) + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -279,14 +447,19 @@ func (m *metricOptionalMetric) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metricOptionalMetric) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.Gauge().DataPoints().At(i).SetDoubleValue(m.data.Gauge().DataPoints().At(i).DoubleValue() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetricOptionalMetric(cfg MetricConfig) metricOptionalMetric { - m := metricOptionalMetric{config: cfg} +func newMetricOptionalMetric(cfg MetricConfig, acfg AttributesConfig) metricOptionalMetric { + m := metricOptionalMetric{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -295,13 +468,16 @@ func newMetricOptionalMetric(cfg MetricConfig) metricOptionalMetric { } type metricOptionalMetricEmptyUnit struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []float64 // slice containing number of aggregated datapoints at each index } // init fills optional.metric.empty_unit metric with initial data. func (m *metricOptionalMetricEmptyUnit) init() { + m.aggDataPoints = nil m.data.SetName("optional.metric.empty_unit") m.data.SetDescription("[DEPRECATED] Gauge double metric disabled by default.") m.data.SetUnit("") @@ -313,12 +489,44 @@ func (m *metricOptionalMetricEmptyUnit) recordDataPoint(start pcommon.Timestamp, if !m.config.Enabled { return } - dp := m.data.Gauge().DataPoints().AppendEmpty() + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) + + if m.attributes.StringAttr.Enabled { + dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) + } + if m.attributes.BooleanAttr.Enabled { + dp.Attributes().PutBool("boolean_attr", booleanAttrAttributeValue) + } + + var s string + dps := m.data.Gauge().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.SetDoubleValue(dpi.DoubleValue() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.DoubleValue() > val { + dpi.SetDoubleValue(val) + } + return + case AggregationStrategyMax: + if dpi.DoubleValue() < val { + dpi.SetDoubleValue(val) + } + return + } + } + } + dp.SetDoubleValue(val) - dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) - dp.Attributes().PutBool("boolean_attr", booleanAttrAttributeValue) + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -331,14 +539,19 @@ func (m *metricOptionalMetricEmptyUnit) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metricOptionalMetricEmptyUnit) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.Gauge().DataPoints().At(i).SetDoubleValue(m.data.Gauge().DataPoints().At(i).DoubleValue() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetricOptionalMetricEmptyUnit(cfg MetricConfig) metricOptionalMetricEmptyUnit { - m := metricOptionalMetricEmptyUnit{config: cfg} +func newMetricOptionalMetricEmptyUnit(cfg MetricConfig, acfg AttributesConfig) metricOptionalMetricEmptyUnit { + m := metricOptionalMetricEmptyUnit{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -407,11 +620,11 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings scraper.Settings, opti startTime: pcommon.NewTimestampFromTime(time.Now()), metricsBuffer: pmetric.NewMetrics(), buildInfo: settings.BuildInfo, - metricDefaultMetric: newMetricDefaultMetric(mbc.Metrics.DefaultMetric), - metricDefaultMetricToBeRemoved: newMetricDefaultMetricToBeRemoved(mbc.Metrics.DefaultMetricToBeRemoved), - metricMetricInputType: newMetricMetricInputType(mbc.Metrics.MetricInputType), - metricOptionalMetric: newMetricOptionalMetric(mbc.Metrics.OptionalMetric), - metricOptionalMetricEmptyUnit: newMetricOptionalMetricEmptyUnit(mbc.Metrics.OptionalMetricEmptyUnit), + metricDefaultMetric: newMetricDefaultMetric(mbc.Metrics.DefaultMetric, mbc.Attributes), + metricDefaultMetricToBeRemoved: newMetricDefaultMetricToBeRemoved(mbc.Metrics.DefaultMetricToBeRemoved, mbc.Attributes), + metricMetricInputType: newMetricMetricInputType(mbc.Metrics.MetricInputType, mbc.Attributes), + metricOptionalMetric: newMetricOptionalMetric(mbc.Metrics.OptionalMetric, mbc.Attributes), + metricOptionalMetricEmptyUnit: newMetricOptionalMetricEmptyUnit(mbc.Metrics.OptionalMetricEmptyUnit, mbc.Attributes), resourceAttributeIncludeFilter: make(map[string]filter.Filter), resourceAttributeExcludeFilter: make(map[string]filter.Filter), } @@ -572,11 +785,13 @@ func (mb *MetricsBuilder) Emit(options ...ResourceMetricsOption) pmetric.Metrics // RecordDefaultMetricDataPoint adds a data point to default.metric metric. func (mb *MetricsBuilder) RecordDefaultMetricDataPoint(ts pcommon.Timestamp, val int64, stringAttrAttributeValue string, overriddenIntAttrAttributeValue int64, enumAttrAttributeValue AttributeEnumAttr, sliceAttrAttributeValue []any, mapAttrAttributeValue map[string]any) { mb.metricDefaultMetric.recordDataPoint(mb.startTime, ts, val, stringAttrAttributeValue, overriddenIntAttrAttributeValue, enumAttrAttributeValue.String(), sliceAttrAttributeValue, mapAttrAttributeValue) + return } // RecordDefaultMetricToBeRemovedDataPoint adds a data point to default.metric.to_be_removed metric. func (mb *MetricsBuilder) RecordDefaultMetricToBeRemovedDataPoint(ts pcommon.Timestamp, val float64) { mb.metricDefaultMetricToBeRemoved.recordDataPoint(mb.startTime, ts, val) + return } // RecordMetricInputTypeDataPoint adds a data point to metric.input_type metric. @@ -592,11 +807,13 @@ func (mb *MetricsBuilder) RecordMetricInputTypeDataPoint(ts pcommon.Timestamp, i // RecordOptionalMetricDataPoint adds a data point to optional.metric metric. func (mb *MetricsBuilder) RecordOptionalMetricDataPoint(ts pcommon.Timestamp, val float64, stringAttrAttributeValue string, booleanAttrAttributeValue bool, booleanAttr2AttributeValue bool) { mb.metricOptionalMetric.recordDataPoint(mb.startTime, ts, val, stringAttrAttributeValue, booleanAttrAttributeValue, booleanAttr2AttributeValue) + return } // RecordOptionalMetricEmptyUnitDataPoint adds a data point to optional.metric.empty_unit metric. func (mb *MetricsBuilder) RecordOptionalMetricEmptyUnitDataPoint(ts pcommon.Timestamp, val float64, stringAttrAttributeValue string, booleanAttrAttributeValue bool) { mb.metricOptionalMetricEmptyUnit.recordDataPoint(mb.startTime, ts, val, stringAttrAttributeValue, booleanAttrAttributeValue) + return } // Reset resets metrics builder to its initial state. It should be used when external metrics source is restarted, diff --git a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics_test.go b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics_test.go index 2261134d605..eb41525d326 100644 --- a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics_test.go +++ b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics_test.go @@ -100,6 +100,8 @@ func TestMetricsBuilder(t *testing.T) { defaultMetricsCount++ allMetricsCount++ mb.RecordDefaultMetricDataPoint(ts, 1, "string_attr-val", 19, AttributeEnumAttrRed, []any{"slice_attr-item1", "slice_attr-item2"}, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}) + allMetricsCount++ + mb.RecordDefaultMetricDataPoint(ts, 3, "string_attr-val-2", 19, AttributeEnumAttrRed, []any{"slice_attr-item1", "slice_attr-item2"}, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}) defaultMetricsCount++ allMetricsCount++ @@ -108,12 +110,18 @@ func TestMetricsBuilder(t *testing.T) { defaultMetricsCount++ allMetricsCount++ mb.RecordMetricInputTypeDataPoint(ts, "1", "string_attr-val", 19, AttributeEnumAttrRed, []any{"slice_attr-item1", "slice_attr-item2"}, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}) + allMetricsCount++ + mb.RecordMetricInputTypeDataPoint(ts, "3", "string_attr-val-2", 19, AttributeEnumAttrRed, []any{"slice_attr-item1", "slice_attr-item2"}, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}) allMetricsCount++ mb.RecordOptionalMetricDataPoint(ts, 1, "string_attr-val", true, false) + allMetricsCount++ + mb.RecordOptionalMetricDataPoint(ts, 3, "string_attr-val-2", true, false) allMetricsCount++ mb.RecordOptionalMetricEmptyUnitDataPoint(ts, 1, "string_attr-val", true) + allMetricsCount++ + mb.RecordOptionalMetricEmptyUnitDataPoint(ts, 3, "string_attr-val-2", true) rb := mb.NewResourceBuilder() rb.SetMapResourceAttr(map[string]any{"key1": "map.resource.attr-val1", "key2": "map.resource.attr-val2"}) @@ -141,7 +149,7 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, defaultMetricsCount, ms.Len()) } if tt.metricsSet == testDataSetAll { - assert.Equal(t, allMetricsCount, ms.Len()) + assert.Equal(t, (allMetricsCount/2)+1, ms.Len()) } validatedMetrics := make(map[string]bool) for i := 0; i < ms.Len(); i++ { @@ -150,113 +158,193 @@ func TestMetricsBuilder(t *testing.T) { assert.False(t, validatedMetrics["default.metric"], "Found a duplicate in the metrics slice: default.metric") validatedMetrics["default.metric"] = true assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) - assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + dp := ms.At(i).Sum().DataPoints().At(0) + if tt.name == "all_set" { + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + } else { + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(4), dp.IntValue()) + } assert.Equal(t, "Monotonic cumulative sum int metric enabled by default.", ms.At(i).Description()) assert.Equal(t, "s", ms.At(i).Unit()) assert.True(t, ms.At(i).Sum().IsMonotonic()) assert.Equal(t, pmetric.AggregationTemporalityCumulative, ms.At(i).Sum().AggregationTemporality()) - dp := ms.At(i).Sum().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) - assert.Equal(t, int64(1), dp.IntValue()) attrVal, ok := dp.Attributes().Get("string_attr") - assert.True(t, ok) - assert.Equal(t, "string_attr-val", attrVal.Str()) + if !mb.config.Attributes.StringAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "string_attr-val", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("state") - assert.True(t, ok) - assert.EqualValues(t, 19, attrVal.Int()) + if !mb.config.Attributes.OverriddenIntAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.EqualValues(t, 19, attrVal.Int()) + } attrVal, ok = dp.Attributes().Get("enum_attr") - assert.True(t, ok) - assert.Equal(t, "red", attrVal.Str()) + if !mb.config.Attributes.EnumAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "red", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("slice_attr") - assert.True(t, ok) - assert.Equal(t, []any{"slice_attr-item1", "slice_attr-item2"}, attrVal.Slice().AsRaw()) + if !mb.config.Attributes.SliceAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, []any{"slice_attr-item1", "slice_attr-item2"}, attrVal.Slice().AsRaw()) + } attrVal, ok = dp.Attributes().Get("map_attr") - assert.True(t, ok) - assert.Equal(t, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}, attrVal.Map().AsRaw()) + if !mb.config.Attributes.MapAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}, attrVal.Map().AsRaw()) + } case "default.metric.to_be_removed": assert.False(t, validatedMetrics["default.metric.to_be_removed"], "Found a duplicate in the metrics slice: default.metric.to_be_removed") validatedMetrics["default.metric.to_be_removed"] = true assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + dp := ms.At(i).Sum().DataPoints().At(0) assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) assert.Equal(t, "[DEPRECATED] Non-monotonic delta sum double metric enabled by default.", ms.At(i).Description()) assert.Equal(t, "s", ms.At(i).Unit()) assert.False(t, ms.At(i).Sum().IsMonotonic()) assert.Equal(t, pmetric.AggregationTemporalityDelta, ms.At(i).Sum().AggregationTemporality()) - dp := ms.At(i).Sum().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) - assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) case "metric.input_type": assert.False(t, validatedMetrics["metric.input_type"], "Found a duplicate in the metrics slice: metric.input_type") validatedMetrics["metric.input_type"] = true assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) - assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + dp := ms.At(i).Sum().DataPoints().At(0) + if tt.name == "all_set" { + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + } else { + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(4), dp.IntValue()) + } assert.Equal(t, "Monotonic cumulative sum int metric with string input_type enabled by default.", ms.At(i).Description()) assert.Equal(t, "s", ms.At(i).Unit()) assert.True(t, ms.At(i).Sum().IsMonotonic()) assert.Equal(t, pmetric.AggregationTemporalityCumulative, ms.At(i).Sum().AggregationTemporality()) - dp := ms.At(i).Sum().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) - assert.Equal(t, int64(1), dp.IntValue()) attrVal, ok := dp.Attributes().Get("string_attr") - assert.True(t, ok) - assert.Equal(t, "string_attr-val", attrVal.Str()) + if !mb.config.Attributes.StringAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "string_attr-val", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("state") - assert.True(t, ok) - assert.EqualValues(t, 19, attrVal.Int()) + if !mb.config.Attributes.OverriddenIntAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.EqualValues(t, 19, attrVal.Int()) + } attrVal, ok = dp.Attributes().Get("enum_attr") - assert.True(t, ok) - assert.Equal(t, "red", attrVal.Str()) + if !mb.config.Attributes.EnumAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "red", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("slice_attr") - assert.True(t, ok) - assert.Equal(t, []any{"slice_attr-item1", "slice_attr-item2"}, attrVal.Slice().AsRaw()) + if !mb.config.Attributes.SliceAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, []any{"slice_attr-item1", "slice_attr-item2"}, attrVal.Slice().AsRaw()) + } attrVal, ok = dp.Attributes().Get("map_attr") - assert.True(t, ok) - assert.Equal(t, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}, attrVal.Map().AsRaw()) + if !mb.config.Attributes.MapAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, map[string]any{"key1": "map_attr-val1", "key2": "map_attr-val2"}, attrVal.Map().AsRaw()) + } case "optional.metric": assert.False(t, validatedMetrics["optional.metric"], "Found a duplicate in the metrics slice: optional.metric") validatedMetrics["optional.metric"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) - assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + dp := ms.At(i).Gauge().DataPoints().At(0) + if tt.name == "all_set" { + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + } else { + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(4), dp.DoubleValue(), 0.01) + } assert.Equal(t, "[DEPRECATED] Gauge double metric disabled by default.", ms.At(i).Description()) assert.Equal(t, "1", ms.At(i).Unit()) - dp := ms.At(i).Gauge().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) - assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) attrVal, ok := dp.Attributes().Get("string_attr") - assert.True(t, ok) - assert.Equal(t, "string_attr-val", attrVal.Str()) + if !mb.config.Attributes.StringAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "string_attr-val", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("boolean_attr") - assert.True(t, ok) - assert.True(t, attrVal.Bool()) + if !mb.config.Attributes.BooleanAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.True(t, attrVal.Bool()) + } attrVal, ok = dp.Attributes().Get("boolean_attr2") - assert.True(t, ok) - assert.False(t, attrVal.Bool()) + if !mb.config.Attributes.BooleanAttr2.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.False(t, attrVal.Bool()) + } case "optional.metric.empty_unit": assert.False(t, validatedMetrics["optional.metric.empty_unit"], "Found a duplicate in the metrics slice: optional.metric.empty_unit") validatedMetrics["optional.metric.empty_unit"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) - assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + dp := ms.At(i).Gauge().DataPoints().At(0) + if tt.name == "all_set" { + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + } else { + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(4), dp.DoubleValue(), 0.01) + } assert.Equal(t, "[DEPRECATED] Gauge double metric disabled by default.", ms.At(i).Description()) assert.Empty(t, ms.At(i).Unit()) - dp := ms.At(i).Gauge().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) - assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) attrVal, ok := dp.Attributes().Get("string_attr") - assert.True(t, ok) - assert.Equal(t, "string_attr-val", attrVal.Str()) + if !mb.config.Attributes.StringAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Equal(t, "string_attr-val", attrVal.Str()) + } attrVal, ok = dp.Attributes().Get("boolean_attr") - assert.True(t, ok) - assert.True(t, attrVal.Bool()) + if !mb.config.Attributes.BooleanAttr.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.True(t, attrVal.Bool()) + } } } }) diff --git a/cmd/mdatagen/internal/samplescraper/internal/metadata/testdata/config.yaml b/cmd/mdatagen/internal/samplescraper/internal/metadata/testdata/config.yaml index c4b7edd6568..9926e120aac 100644 --- a/cmd/mdatagen/internal/samplescraper/internal/metadata/testdata/config.yaml +++ b/cmd/mdatagen/internal/samplescraper/internal/metadata/testdata/config.yaml @@ -1,5 +1,20 @@ default: all_set: + attributes: + boolean_attr: + enabled: true + boolean_attr2: + enabled: true + enum_attr: + enabled: true + map_attr: + enabled: true + overridden_int_attr: + enabled: true + slice_attr: + enabled: true + string_attr: + enabled: true metrics: default.metric: enabled: true @@ -29,6 +44,21 @@ all_set: string.resource.attr_to_be_removed: enabled: true none_set: + attributes: + boolean_attr: + enabled: false + boolean_attr2: + enabled: false + enum_attr: + enabled: false + map_attr: + enabled: false + overridden_int_attr: + enabled: false + slice_attr: + enabled: false + string_attr: + enabled: false metrics: default.metric: enabled: false diff --git a/cmd/mdatagen/internal/samplescraper/metadata.yaml b/cmd/mdatagen/internal/samplescraper/metadata.yaml index 01105f11de0..8a3a8690cc9 100644 --- a/cmd/mdatagen/internal/samplescraper/metadata.yaml +++ b/cmd/mdatagen/internal/samplescraper/metadata.yaml @@ -70,34 +70,41 @@ attributes: string_attr: description: Attribute with any string value. type: string + enabled: false overridden_int_attr: name_override: state description: Integer attribute with overridden name. type: int + enabled: true enum_attr: description: Attribute with a known set of string values. type: string enum: [red, green, blue] + enabled: true boolean_attr: description: Attribute with a boolean value. type: bool + enabled: true # This 2nd boolean attribute allows us to test both values for boolean attributes, # as test values are based on the parity of the attribute name length. boolean_attr2: description: Another attribute with a boolean value. type: bool + enabled: true slice_attr: description: Attribute with a slice value. type: slice + enabled: true map_attr: description: Attribute with a map value. type: map + enabled: true metrics: default.metric: diff --git a/cmd/mdatagen/internal/templates/config.go.tmpl b/cmd/mdatagen/internal/templates/config.go.tmpl index 58f7b2229cc..b5ef5eda3e5 100644 --- a/cmd/mdatagen/internal/templates/config.go.tmpl +++ b/cmd/mdatagen/internal/templates/config.go.tmpl @@ -3,6 +3,8 @@ package {{ .Package }} import ( + "fmt" + "go.opentelemetry.io/collector/confmap" {{ if and .Metrics .ResourceAttributes -}} "go.opentelemetry.io/collector/filter" @@ -10,9 +12,11 @@ import ( ) {{ if .Metrics -}} + // MetricConfig provides common config for a particular metric. type MetricConfig struct { - Enabled bool `mapstructure:"enabled"` + Enabled bool `mapstructure:"enabled"` + AggregationStrategy string `mapstructure:"aggregation_strategy"` enabledSetByUser bool } @@ -25,10 +29,24 @@ func (ms *MetricConfig) Unmarshal(parser *confmap.Conf) error { if err != nil { return err } + + if ms.AggregationStrategy != AggregationStrategySum && + ms.AggregationStrategy != AggregationStrategyAvg && + ms.AggregationStrategy != AggregationStrategyMin && + ms.AggregationStrategy != AggregationStrategyMax { + return fmt.Errorf("invalid aggregation strategy set: '%v'", ms.AggregationStrategy) + } + ms.enabledSetByUser = parser.IsSet("enabled") return nil } +// AttributeConfig holds configuration information for a particular metric. +type AttributeConfig struct { + Enabled bool `mapstructure:"enabled"` +} + + // MetricsConfig provides config for {{ .Type }} metrics. type MetricsConfig struct { {{- range $name, $metric := .Metrics }} @@ -36,11 +54,36 @@ type MetricsConfig struct { {{- end }} } +// AttributesConfig is the collected configuration for all attributes in the +// component +type AttributesConfig struct { + {{- range $name, $attribute := .Attributes }} + {{ $name.Render }} AttributeConfig `mapstructure:"{{ $name }}"` + {{- end }} +} + + +func DefaultAttributesConfig() AttributesConfig { + return AttributesConfig{ + {{- range $name, $attribute := .Attributes }} + {{ $name.Render }}: AttributeConfig{ + Enabled: {{ $attribute.Enabled }}, + }, + {{- end }} + } +} + func DefaultMetricsConfig() MetricsConfig { return MetricsConfig{ {{- range $name, $metric := .Metrics }} {{ $name.Render }}: MetricConfig{ Enabled: {{ $metric.Enabled }}, + {{ if eq $metric.Data.Type "Sum" -}} + AggregationStrategy: AggregationStrategySum, + {{- end }} + {{- if eq $metric.Data.Type "Gauge" -}} + AggregationStrategy: AggregationStrategyAvg, + {{- end }} }, {{- end }} } @@ -145,6 +188,7 @@ func DefaultResourceAttributesConfig() ResourceAttributesConfig { // MetricsBuilderConfig is a configuration for {{ .Type }} metrics builder. type MetricsBuilderConfig struct { Metrics MetricsConfig `mapstructure:"metrics"` + Attributes AttributesConfig `mapstructure:"attributes"` {{- if .ResourceAttributes }} ResourceAttributes ResourceAttributesConfig `mapstructure:"resource_attributes"` {{- end }} @@ -153,6 +197,7 @@ type MetricsBuilderConfig struct { func DefaultMetricsBuilderConfig() MetricsBuilderConfig { return MetricsBuilderConfig { Metrics: DefaultMetricsConfig(), + Attributes: DefaultAttributesConfig(), {{- if .ResourceAttributes }} ResourceAttributes: DefaultResourceAttributesConfig(), {{- end }} diff --git a/cmd/mdatagen/internal/templates/config_test.go.tmpl b/cmd/mdatagen/internal/templates/config_test.go.tmpl index 781a115c4a0..b5a424f4ed1 100644 --- a/cmd/mdatagen/internal/templates/config_test.go.tmpl +++ b/cmd/mdatagen/internal/templates/config_test.go.tmpl @@ -28,10 +28,23 @@ func TestMetricsBuilderConfig(t *testing.T) { name: "all_set", want: MetricsBuilderConfig{ Metrics: MetricsConfig{ - {{- range $name, $_ := .Metrics }} - {{ $name.Render }}: MetricConfig{Enabled: true}, + {{- range $name, $metric := .Metrics }} + {{ $name.Render }}: MetricConfig{ + Enabled: true, + {{- if eq $metric.Data.Type "Sum" }} + AggregationStrategy: "sum"}, + {{- else -}} + AggregationStrategy: "avg"}, + {{- end }} {{- end }} }, + Attributes: AttributesConfig{ + {{- range $name, $_ := .Attributes }} + {{ $name.Render }}: AttributeConfig{ + Enabled: true, + }, + {{- end }} + }, {{- if .ResourceAttributes }} ResourceAttributes: ResourceAttributesConfig{ {{- range $name, $_ := .ResourceAttributes }} @@ -45,10 +58,23 @@ func TestMetricsBuilderConfig(t *testing.T) { name: "none_set", want: MetricsBuilderConfig{ Metrics: MetricsConfig{ - {{- range $name, $_ := .Metrics }} - {{ $name.Render }}: MetricConfig{Enabled: false}, + {{- range $name, $metric := .Metrics }} + {{ $name.Render }}: MetricConfig{ + Enabled: false, + {{- if eq $metric.Data.Type "Sum" }} + AggregationStrategy: "sum"}, + {{- else -}} + AggregationStrategy: "avg"}, + {{- end }} {{- end }} }, + Attributes: AttributesConfig{ + {{- range $name, $_ := .Attributes }} + {{ $name.Render }}: AttributeConfig{ + Enabled: false, + }, + {{- end }} + }, {{- if .ResourceAttributes }} ResourceAttributes: ResourceAttributesConfig{ {{- range $name, $_ := .ResourceAttributes }} @@ -92,6 +118,55 @@ func loadLogsBuilderConfig(t *testing.T, name string) LogsBuilderConfig { } {{- end }} +{{ if .Attributes }} +func TestAttributesConfig(t *testing.T) { + tests := []struct { + name string + want AttributesConfig + }{ + { + name: "default", + want: DefaultAttributesConfig(), + }, + { + name: "all_set", + want: AttributesConfig{ + {{- range $name, $attribute := .Attributes }} + {{ $name.Render }}: AttributeConfig{Enabled: true}, + {{- end }} + }, + }, + { + name: "none_set", + want: AttributesConfig{ + {{- range $name, $attribute := .Attributes }} + {{ $name.Render }}: AttributeConfig{Enabled: false}, + {{- end }} + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := loadAttributesConfig(t, tt.name) + diff := cmp.Diff(tt.want, cfg, cmpopts.IgnoreUnexported(AttributeConfig{})) + require.Emptyf(t, diff, "Config mismatch (-expected +actual):\n%s", diff) + }) + } +} + +func loadAttributesConfig(t *testing.T, name string) AttributesConfig { + cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) + require.NoError(t, err) + sub, err := cm.Sub(name) + require.NoError(t, err) + sub, err = sub.Sub("attributes") + require.NoError(t, err) + cfg := DefaultAttributesConfig() + require.NoError(t, sub.Unmarshal(&cfg)) + return cfg +} +{{- end }} + {{ if .ResourceAttributes -}} func TestResourceAttributesConfig(t *testing.T) { tests := []struct { diff --git a/cmd/mdatagen/internal/templates/helper.tmpl b/cmd/mdatagen/internal/templates/helper.tmpl index 2fdca5ec42c..082806b31b9 100644 --- a/cmd/mdatagen/internal/templates/helper.tmpl +++ b/cmd/mdatagen/internal/templates/helper.tmpl @@ -10,6 +10,26 @@ {{- end }} {{- end -}} +{{- define "putMetricAttribute" -}} + if m.attributes.{{ (attributeInfo .).FullName.Render }}.Enabled { + {{- if eq (attributeInfo .).Type.Primitive "[]byte" }} + dp.Attributes().PutEmptyBytes("{{ (attributeInfo .).Name }}").FromRaw({{ .RenderUnexported }}AttributeValue) + {{- else if eq (attributeInfo .).Type.Primitive "[]any" }} + dp.Attributes().PutEmptySlice("{{ (attributeInfo .).Name }}").FromRaw({{ .RenderUnexported }}AttributeValue) + {{- else if eq (attributeInfo .).Type.Primitive "map[string]any" }} + dp.Attributes().PutEmptyMap("{{ (attributeInfo .).Name }}").FromRaw({{ .RenderUnexported }}AttributeValue) + {{- else }} + dp.Attributes().Put{{ (attributeInfo .).Type }}("{{ (attributeInfo .).Name }}", {{ .RenderUnexported }}AttributeValue) + {{- end }} + } +{{- end -}} + {{- define "getAttributeValue" -}} {{ if (attributeInfo .).Enum }}Attribute{{ .Render }}{{ (index (attributeInfo .).Enum 0) | publicVar }}{{ else }}{{ (attributeInfo .).TestValue }}{{ end }} {{- end -}} + +{{- define "getAttributeValueTwo" -}} +{{ if (attributeInfo .).Enum }}Attribute{{ .Render }}{{ (index (attributeInfo .).Enum 0) | publicVar }}{{ else }}{{ (attributeInfo .).TestValueTwo }}{{ end }} +{{- end -}} + + diff --git a/cmd/mdatagen/internal/templates/metrics.go.tmpl b/cmd/mdatagen/internal/templates/metrics.go.tmpl index 7b2be36e36c..d348f957f02 100644 --- a/cmd/mdatagen/internal/templates/metrics.go.tmpl +++ b/cmd/mdatagen/internal/templates/metrics.go.tmpl @@ -29,6 +29,10 @@ import ( type Attribute{{ $name.Render }} int const ( + AggregationStrategySum = "sum" + AggregationStrategyAvg = "avg" + AggregationStrategyMin = "min" + AggregationStrategyMax = "max" _ Attribute{{ $name.Render }} = iota {{- range $info.Enum }} Attribute{{ $name.Render }}{{ . | publicVar }} @@ -96,13 +100,16 @@ func With{{ .Render }}MetricAttribute({{ .RenderUnexported }}AttributeValue {{ ( {{ range $name, $metric := .Metrics -}} type metric{{ $name.Render }} struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + attributes AttributesConfig // attributes configured by the user. + capacity int // max observed number of data points added to the metric. + aggDataPoints []{{ $metric.Data.MetricValueType.BasicType }} // slice containing number of aggregated datapoints at each index } // init fills {{ $name }} metric with initial data. func (m *metric{{ $name.Render }}) init() { + m.aggDataPoints = nil m.data.SetName("{{ $name }}") m.data.SetDescription("{{ $metric.Description }}") m.data.SetUnit("{{ $metric.Unit }}") @@ -120,24 +127,50 @@ func (m *metric{{ $name.Render }}) init() { func (m *metric{{ $name.Render }}) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val {{ $metric.Data.MetricValueType.BasicType }} {{- range $metric.Attributes -}}{{- if not (attributeInfo .).Optional -}}, {{ .RenderUnexported }}AttributeValue {{ (attributeInfo .).Type.Primitive }}{{ end }}{{ end }}{{- if $metric.HasOptionalAttribute $.Attributes -}}, options ...MetricAttributeOption{{- end -}}) { - if !m.config.Enabled { - return - } - dp := m.data.{{ $metric.Data.Type }}().DataPoints().AppendEmpty() + if !m.config.Enabled { + return + } + dp := pmetric.NewNumberDataPoint() dp.SetStartTimestamp(start) dp.SetTimestamp(ts) - dp.Set{{ $metric.Data.MetricValueType }}Value(val) - {{- range $metric.Attributes }} - {{- if not (attributeInfo .).Optional -}} - {{- template "putAttribute" . -}} + {{ range $metric.Attributes -}} + {{ if not (attributeInfo .).Optional }} + {{ template "putMetricAttribute" . -}} {{- end }} {{- end }} - {{- if $metric.HasOptionalAttribute $.Attributes }} + var s string + dps := m.data.{{ $metric.Data.Type }}().DataPoints() + for i := 0; i < dps.Len(); i++ { + dpi := dps.At(i) + if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() { + switch s = m.config.AggregationStrategy; s { + case AggregationStrategySum, AggregationStrategyAvg: + dpi.Set{{ $metric.Data.MetricValueType }}Value(dpi.{{ $metric.Data.MetricValueType }}Value() + val) + m.aggDataPoints[i] += 1 + return + case AggregationStrategyMin: + if dpi.{{ $metric.Data.MetricValueType }}Value() > val { + dpi.Set{{ $metric.Data.MetricValueType }}Value(val) + } + return + case AggregationStrategyMax: + if dpi.{{ $metric.Data.MetricValueType }}Value() < val { + dpi.Set{{ $metric.Data.MetricValueType }}Value(val) + } + return + } + } + } + + dp.Set{{ $metric.Data.MetricValueType }}Value(val) + {{- if $metric.HasOptionalAttribute $.Attributes }} for _, op := range options { op.apply(dp) } - {{- end }} + {{- end }} + m.aggDataPoints = append(m.aggDataPoints, 1) + dp.MoveTo(dps.AppendEmpty()) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -150,14 +183,19 @@ func (m *metric{{ $name.Render }}) updateCapacity() { // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. func (m *metric{{ $name.Render }}) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.{{ $metric.Data.Type }}().DataPoints().Len() > 0 { + if m.config.AggregationStrategy == AggregationStrategyAvg { + for i, aggCount := range m.aggDataPoints { + m.data.{{ $metric.Data.Type }}().DataPoints().At(i).Set{{ $metric.Data.MetricValueType }}Value(m.data.{{ $metric.Data.Type }}().DataPoints().At(i).{{ $metric.Data.MetricValueType }}Value() / aggCount) + } + } m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) m.init() } } -func newMetric{{ $name.Render }}(cfg MetricConfig) metric{{ $name.Render }} { - m := metric{{ $name.Render }}{config: cfg} +func newMetric{{ $name.Render }}(cfg MetricConfig, acfg AttributesConfig) metric{{ $name.Render }} { + m := metric{{ $name.Render }}{config: cfg, attributes: acfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -244,7 +282,7 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings {{ .Status.Class }}.Se metricsBuffer: pmetric.NewMetrics(), buildInfo: settings.BuildInfo, {{- range $name, $metric := .Metrics }} - metric{{ $name.Render }}: newMetric{{ $name.Render }}(mbc.Metrics.{{ $name.Render }}), + metric{{ $name.Render }}: newMetric{{ $name.Render }}(mbc.Metrics.{{ $name.Render }}, mbc.Attributes), {{- end }} {{ if .ResourceAttributes -}} resourceAttributeIncludeFilter: make(map[string]filter.Filter), @@ -408,6 +446,8 @@ func (mb *MetricsBuilder) Record{{ $name.Render }}DataPoint(ts pcommon.Timestamp ) {{- if $metric.Data.HasMetricInputType }} return nil + {{- else }} + return {{- end }} } {{ end }} diff --git a/cmd/mdatagen/internal/templates/metrics_test.go.tmpl b/cmd/mdatagen/internal/templates/metrics_test.go.tmpl index d4ec81f66ec..49d70216427 100644 --- a/cmd/mdatagen/internal/templates/metrics_test.go.tmpl +++ b/cmd/mdatagen/internal/templates/metrics_test.go.tmpl @@ -122,7 +122,7 @@ func TestMetricsBuilder(t *testing.T) { mb.Record{{ $name.Render }}DataPoint(ts, {{ if $metric.Data.HasMetricInputType }}"1"{{ else }}1{{ end }} {{- range $metric.Attributes -}} {{- if not (attributeInfo .).Optional -}} - , {{- template "getAttributeValue" . -}} + , {{- template "getAttributeValue" . -}} {{- end -}} {{- end -}} {{- range $metric.Attributes -}} @@ -131,6 +131,23 @@ func TestMetricsBuilder(t *testing.T) { {{- end -}} {{- end -}} ) + {{- range $metric.Attributes -}} + {{ if not (attributeInfo .).Enabled }} + allMetricsCount++ + mb.Record{{ $name.Render }}DataPoint(ts, {{ if $metric.Data.HasMetricInputType }}"3"{{ else }}3{{ end }} + {{- range $metric.Attributes -}} + {{- if not (attributeInfo .).Optional -}} + , {{ if (attributeInfo .).Enabled }}{{- template "getAttributeValue" . -}}{{ else }}{{- template "getAttributeValueTwo" . -}}{{ end }} + {{- end -}} + {{- end -}} + {{- range $metric.Attributes -}} + {{- if (attributeInfo .).Optional -}} + , With{{ .Render }}MetricAttribute({{ if (attributeInfo .).Enabled }}{{- template "getAttributeValue" . -}}{{ else }}{{- template "getAttributeValueTwo" . -}}{{ end }}) + {{- end -}} + {{- end -}} + ) + {{- end -}} + {{- end -}} {{- end }} {{ if .ResourceAttributes }} @@ -162,7 +179,7 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, defaultMetricsCount, ms.Len()) } if tt.metricsSet == testDataSetAll { - assert.Equal(t, allMetricsCount, ms.Len()) + assert.Equal(t, (allMetricsCount / 2) + 1, ms.Len()) } validatedMetrics := make(map[string]bool) for i := 0; i < ms.Len(); i++ { @@ -172,7 +189,31 @@ func TestMetricsBuilder(t *testing.T) { assert.False(t, validatedMetrics["{{ $name }}"], "Found a duplicate in the metrics slice: {{ $name }}") validatedMetrics["{{ $name }}"] = true assert.Equal(t, pmetric.MetricType{{ $metric.Data.Type }}, ms.At(i).Type()) + dp := ms.At(i).{{ $metric.Data.Type }}().DataPoints().At(0) + {{- if eq $name.Render "DefaultMetricToBeRemoved" }} assert.Equal(t, 1, ms.At(i).{{ $metric.Data.Type }}().DataPoints().Len()) + {{- if eq $metric.Data.MetricValueType.BasicType "float64" }} + assert.InDelta(t, {{ $metric.Data.MetricValueType.BasicType }}(1), dp.{{ $metric.Data.MetricValueType }}Value(), 0.01) + {{- else }} + assert.Equal(t, {{ $metric.Data.MetricValueType.BasicType }}(1), dp.{{ $metric.Data.MetricValueType }}Value()) + {{- end }} + {{- else }} + if tt.name == "all_set" { + assert.Equal(t, 2, ms.At(i).{{ $metric.Data.Type }}().DataPoints().Len()) + {{- if eq $metric.Data.MetricValueType.BasicType "float64" }} + assert.InDelta(t, {{ $metric.Data.MetricValueType.BasicType }}(1), dp.{{ $metric.Data.MetricValueType }}Value(), 0.01) + {{- else }} + assert.Equal(t, {{ $metric.Data.MetricValueType.BasicType }}(1), dp.{{ $metric.Data.MetricValueType }}Value()) + {{- end }} + } else { + assert.Equal(t, 1, ms.At(i).{{ $metric.Data.Type }}().DataPoints().Len()) + {{- if eq $metric.Data.MetricValueType.BasicType "float64" }} + assert.InDelta(t, {{ $metric.Data.MetricValueType.BasicType }}(4), dp.{{ $metric.Data.MetricValueType }}Value(), 0.01) + {{- else }} + assert.Equal(t, {{ $metric.Data.MetricValueType.BasicType }}(4), dp.{{ $metric.Data.MetricValueType }}Value()) + {{- end }} + } + {{- end }} assert.Equal(t, "{{ $metric.Description }}", ms.At(i).Description()) {{- if len $metric.Unit}} assert.Equal(t, "{{ $metric.Unit }}", ms.At(i).Unit()) @@ -185,27 +226,25 @@ func TestMetricsBuilder(t *testing.T) { {{- if $metric.Data.HasAggregated }} assert.Equal(t, pmetric.AggregationTemporality{{ $metric.Data.AggregationTemporality }}, ms.At(i).{{ $metric.Data.Type }}().AggregationTemporality()) {{- end }} - dp := ms.At(i).{{ $metric.Data.Type }}().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueType{{ $metric.Data.MetricValueType }}, dp.ValueType()) - {{- if eq $metric.Data.MetricValueType.BasicType "float64" }} - assert.InDelta(t, {{ $metric.Data.MetricValueType.BasicType }}(1), dp.{{ $metric.Data.MetricValueType }}Value(), 0.01) - {{- else }} - assert.Equal(t, {{ $metric.Data.MetricValueType.BasicType }}(1), dp.{{ $metric.Data.MetricValueType }}Value()) - {{- end }} {{- range $i, $attr := $metric.Attributes }} attrVal, ok {{ if eq $i 0 }}:{{ end }}= dp.Attributes().Get("{{ (attributeInfo $attr).Name }}") - assert.True(t, ok) - {{- if eq (attributeInfo $attr).Type.String "Bool"}} - assert.{{- if eq (attributeInfo $attr).TestValue "true" }}True{{ else }}False{{- end }}(t, attrVal.{{ (attributeInfo $attr).Type }}() - {{- else if eq (attributeInfo $attr).Type.String "Int"}} - assert.EqualValues(t, {{ (attributeInfo $attr).TestValue }}, attrVal.{{ (attributeInfo $attr).Type }}() - {{- else }} - assert.Equal(t, {{ (attributeInfo $attr).TestValue }}, attrVal.{{ (attributeInfo $attr).Type }}() - {{- end }} - {{- if or (eq (attributeInfo $attr).Type.String "Slice") (eq (attributeInfo $attr).Type.String "Map")}}.AsRaw(){{ end }}) + if !mb.config.Attributes.{{ (attributeInfo $attr).FullName.Render }}.Enabled { + assert.False(t, ok) + } else { + assert.True(t, ok) + {{- if eq (attributeInfo $attr).Type.String "Bool"}} + assert.{{- if eq (attributeInfo $attr).TestValue "true" }}True{{ else }}False{{- end }}(t, attrVal.{{ (attributeInfo $attr).Type }}() + {{- else if eq (attributeInfo $attr).Type.String "Int"}} + assert.EqualValues(t, {{ (attributeInfo $attr).TestValue }}, attrVal.{{ (attributeInfo $attr).Type }}() + {{- else }} + assert.Equal(t, {{ (attributeInfo $attr).TestValue }}, attrVal.{{ (attributeInfo $attr).Type }}() + {{- end }} + {{- if or (eq (attributeInfo $attr).Type.String "Slice") (eq (attributeInfo $attr).Type.String "Map")}}.AsRaw(){{ end }}) + } {{- end }} {{- end }} } diff --git a/cmd/mdatagen/internal/templates/testdata/config.yaml.tmpl b/cmd/mdatagen/internal/templates/testdata/config.yaml.tmpl index 1edabcba2c4..e0076d98e17 100644 --- a/cmd/mdatagen/internal/templates/testdata/config.yaml.tmpl +++ b/cmd/mdatagen/internal/templates/testdata/config.yaml.tmpl @@ -1,5 +1,12 @@ default: all_set: + {{- if .Attributes }} + attributes: + {{- range $name, $_ := .Attributes }} + {{ $name }}: + enabled: true + {{- end }} + {{- end }} {{- if .Metrics }} metrics: {{- range $name, $_ := .Metrics }} @@ -22,6 +29,13 @@ all_set: {{- end }} {{- end }} none_set: + {{- if .Attributes }} + attributes: + {{- range $name, $_ := .Attributes }} + {{ $name }}: + enabled: false + {{- end }} + {{- end }} {{- if .Metrics }} metrics: {{- range $name, $_ := .Metrics }} diff --git a/cmd/mdatagen/internal/testdata/invalid_type_attr.yaml b/cmd/mdatagen/internal/testdata/invalid_type_attr.yaml index 47eb3f1cff2..739192bbcaa 100644 --- a/cmd/mdatagen/internal/testdata/invalid_type_attr.yaml +++ b/cmd/mdatagen/internal/testdata/invalid_type_attr.yaml @@ -13,6 +13,7 @@ attributes: used_attr: description: Used attribute. type: invalidtype + enabled: true metrics: metric: diff --git a/cmd/mdatagen/internal/testdata/no_description_attr.yaml b/cmd/mdatagen/internal/testdata/no_description_attr.yaml index 1942f74e111..d3c0b0bcd1c 100644 --- a/cmd/mdatagen/internal/testdata/no_description_attr.yaml +++ b/cmd/mdatagen/internal/testdata/no_description_attr.yaml @@ -17,6 +17,7 @@ status: attributes: string_attr: type: string + enabled: true metrics: default.metric: diff --git a/cmd/mdatagen/internal/testdata/no_type_attr.yaml b/cmd/mdatagen/internal/testdata/no_type_attr.yaml index c267d218e75..56c0e698efe 100644 --- a/cmd/mdatagen/internal/testdata/no_type_attr.yaml +++ b/cmd/mdatagen/internal/testdata/no_type_attr.yaml @@ -12,6 +12,7 @@ status: attributes: used_attr: description: Used attribute. + enabled: true metrics: metric: diff --git a/cmd/mdatagen/internal/testdata/unused_attribute.yaml b/cmd/mdatagen/internal/testdata/unused_attribute.yaml index a3348712f8b..9092a6aa0f7 100644 --- a/cmd/mdatagen/internal/testdata/unused_attribute.yaml +++ b/cmd/mdatagen/internal/testdata/unused_attribute.yaml @@ -12,12 +12,15 @@ status: attributes: used_attr_in_metrics_section: description: Used attribute. + enabled: true type: string used_attr_in_telemetry_section: + enabled: true description: Used attribute. type: string unused_attr: + enabled: true name_override: state description: Unused attribute. type: string @@ -39,4 +42,4 @@ telemetry: unit: "1" gauge: value_type: double - attributes: [used_attr_in_telemetry_section] \ No newline at end of file + attributes: [used_attr_in_telemetry_section] diff --git a/cmd/mdatagen/internal/testdata/with_optional_attribute.yaml b/cmd/mdatagen/internal/testdata/with_optional_attribute.yaml index 5be0edd2960..ae640691b29 100644 --- a/cmd/mdatagen/internal/testdata/with_optional_attribute.yaml +++ b/cmd/mdatagen/internal/testdata/with_optional_attribute.yaml @@ -12,6 +12,7 @@ attributes: description: Optional int attr. type: string optional: true + enabled: true metrics: metric: diff --git a/cmd/mdatagen/internal/testdata/with_telemetry.yaml b/cmd/mdatagen/internal/testdata/with_telemetry.yaml index 6d203ac9875..cdfd150e3ad 100644 --- a/cmd/mdatagen/internal/testdata/with_telemetry.yaml +++ b/cmd/mdatagen/internal/testdata/with_telemetry.yaml @@ -4,10 +4,13 @@ status: class: receiver stability: beta: [traces, logs, metrics] + attributes: name: description: Name of sampling decision type: string + enabled: true + telemetry: metrics: sampling_decision_latency: diff --git a/cmd/mdatagen/metadata-schema.yaml b/cmd/mdatagen/metadata-schema.yaml index 4de8ffc1e26..f793ed4d1cd 100644 --- a/cmd/mdatagen/metadata-schema.yaml +++ b/cmd/mdatagen/metadata-schema.yaml @@ -80,6 +80,9 @@ attributes: type: # Optional: indicates whether the attribute is optional or not. optional: + # Required: whether the metric attribute is enabled by default for the + # receiver instance. This only affects attributes present on metrics. + enabled: bool # Optional: map of metric names with the key being the metric name and value # being described below. @@ -118,6 +121,10 @@ metrics: input_type: string # Optional: array of attributes that were defined in the attributes section that are emitted by this metric. attributes: [string] + # Optional: strategy for approach to spacial reaggregation of metrics. + # Spacial reaggregation refers to metric aggregation based on present + # attributes, or dimensions, for a given metric. + aggregation_strategy: # Optional: map of event names with the key being the event name and value # being described below.