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.go b/cmd/mdatagen/internal/loader.go index 161e3bf50a7..1caa25770cc 100644 --- a/cmd/mdatagen/internal/loader.go +++ b/cmd/mdatagen/internal/loader.go @@ -22,6 +22,13 @@ func setAttributesFullName(attrs map[AttributeName]Attribute) { } } +func setResourceAttributesFullName(attrs map[AttributeName]ResourceAttribute) { + for k, v := range attrs { + v.FullName = k + attrs[k] = v + } +} + type TemplateContext struct { Metadata // Package name for generated code. @@ -62,7 +69,7 @@ func LoadMetadata(filePath string) (Metadata, error) { } setAttributesFullName(md.Attributes) - setAttributesFullName(md.ResourceAttributes) + setResourceAttributesFullName(md.ResourceAttributes) return md, nil } diff --git a/cmd/mdatagen/internal/loader_test.go b/cmd/mdatagen/internal/loader_test.go index 4ea52e111d9..35d7993e5bf 100644 --- a/cmd/mdatagen/internal/loader_test.go +++ b/cmd/mdatagen/internal/loader_test.go @@ -67,7 +67,7 @@ func TestLoadMetadata(t *testing.T) { Warnings: []string{"Any additional information that should be brought to the consumer's attention"}, UnsupportedPlatforms: []string{"freebsd", "illumos"}, }, - ResourceAttributes: map[AttributeName]Attribute{ + ResourceAttributes: map[AttributeName]ResourceAttribute{ "string.resource.attr": { Description: "Resource attribute with any string value.", Enabled: true, @@ -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{ @@ -224,7 +234,8 @@ func TestLoadMetadata(t *testing.T) { Warnings: Warnings{ IfEnabledNotSet: "This metric will be disabled by default soon.", }, - Attributes: []AttributeName{"string_attr", "overridden_int_attr", "enum_attr", "slice_attr", "map_attr", "optional_int_attr", "optional_string_attr"}, + Attributes: []AttributeName{"string_attr", "overridden_int_attr", "enum_attr", "slice_attr", "map_attr", "optional_int_attr", "optional_string_attr"}, + NumAttributes: 7, }, Unit: strPtr("s"), Sum: &Sum{ @@ -240,7 +251,8 @@ func TestLoadMetadata(t *testing.T) { Warnings: Warnings{ IfConfigured: "This metric is deprecated and will be removed soon.", }, - Attributes: []AttributeName{"string_attr", "boolean_attr", "boolean_attr2", "optional_string_attr"}, + Attributes: []AttributeName{"string_attr", "boolean_attr", "boolean_attr2", "optional_string_attr"}, + NumAttributes: 4, }, Unit: strPtr("1"), Gauge: &Gauge{ @@ -254,7 +266,8 @@ func TestLoadMetadata(t *testing.T) { Warnings: Warnings{ IfConfigured: "This metric is deprecated and will be removed soon.", }, - Attributes: []AttributeName{"string_attr", "boolean_attr"}, + Attributes: []AttributeName{"string_attr", "boolean_attr"}, + NumAttributes: 2, }, Unit: strPtr(""), Gauge: &Gauge{ @@ -280,9 +293,10 @@ func TestLoadMetadata(t *testing.T) { }, "metric.input_type": { Signal: Signal{ - Enabled: true, - Description: "Monotonic cumulative sum int metric with string input_type enabled by default.", - Attributes: []AttributeName{"string_attr", "overridden_int_attr", "enum_attr", "slice_attr", "map_attr"}, + Enabled: true, + Description: "Monotonic cumulative sum int metric with string input_type enabled by default.", + Attributes: []AttributeName{"string_attr", "overridden_int_attr", "enum_attr", "slice_attr", "map_attr"}, + NumAttributes: 5, }, Unit: strPtr("s"), Sum: &Sum{ @@ -454,7 +468,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 +498,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..e25f64271d9 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" ) @@ -28,7 +29,7 @@ type Metadata struct { // SemConvVersion is a version number of OpenTelemetry semantic conventions applied to the scraped metrics. SemConvVersion string `mapstructure:"sem_conv_version"` // ResourceAttributes that can be emitted by the component. - ResourceAttributes map[AttributeName]Attribute `mapstructure:"resource_attributes"` + ResourceAttributes map[AttributeName]ResourceAttribute `mapstructure:"resource_attributes"` // Attributes emitted by one or more metrics. Attributes map[AttributeName]Attribute `mapstructure:"attributes"` // Metrics that can be emitted by the component. @@ -287,7 +288,7 @@ type Warnings struct { IfConfigured string `mapstructure:"if_configured"` } -type Attribute struct { +type AttributeStruct struct { // Description describes the purpose of the attribute. Description string `mapstructure:"description"` // NameOverride can be used to override the attribute name. @@ -310,6 +311,96 @@ type Attribute struct { Optional bool `mapstructure:"optional"` } +type ResourceAttribute AttributeStruct + +func (a *ResourceAttribute) Unmarshal(parser *confmap.Conf) error { + err := parser.Unmarshal(a) + if err != nil { + return err + } + + if !parser.IsSet("enabled") { + return fmt.Errorf("missing field `enabled` for resource attribute") + } + + return nil +} + +// Name returns actual name of the attribute that is set on the metric after applying NameOverride. +func (a ResourceAttribute) Name() AttributeName { + if a.NameOverride != "" { + return AttributeName(a.NameOverride) + } + return a.FullName +} + +func (a ResourceAttribute) TestValue() string { + if a.Enum != nil { + return fmt.Sprintf(`%q`, a.Enum[0]) + } + switch a.Type.ValueType { + case pcommon.ValueTypeEmpty: + return "" + case pcommon.ValueTypeStr: + return fmt.Sprintf(`"%s-val"`, a.FullName) + case pcommon.ValueTypeInt: + return strconv.Itoa(len(a.FullName)) + case pcommon.ValueTypeDouble: + return fmt.Sprintf("%f", 0.1+float64(len(a.FullName))) + case pcommon.ValueTypeBool: + return strconv.FormatBool(len(a.FullName)%2 == 0) + case pcommon.ValueTypeMap: + return fmt.Sprintf(`map[string]any{"key1": "%s-val1", "key2": "%s-val2"}`, a.FullName, a.FullName) + case pcommon.ValueTypeSlice: + return fmt.Sprintf(`[]any{"%s-item1", "%s-item2"}`, a.FullName, a.FullName) + case pcommon.ValueTypeBytes: + return fmt.Sprintf(`[]byte("%s-val")`, a.FullName) + } + return "" +} + +func (a ResourceAttribute) TestValueTwo() string { + if len(a.Enum) > 2 { + fmt.Printf("second %q", a.Enum[1]) + 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 Attribute AttributeStruct + +func (a *Attribute) Unmarshal(parser *confmap.Conf) error { + err := parser.Unmarshal(a) + if err != nil { + return err + } + + // default enabled to true + if !parser.IsSet("enabled") { + a.Enabled = true + } + + return nil +} + // 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 +434,32 @@ func (a Attribute) TestValue() string { return "" } +func (a Attribute) TestValueTwo() string { + if len(a.Enum) > 2 { + fmt.Printf("second %q", a.Enum[1]) + 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"` @@ -361,6 +478,9 @@ type Signal struct { // Attributes is the list of attributes that the signal emits. Attributes []AttributeName `mapstructure:"attributes"` + + // NumAttributes is the length of the attribtues field. + NumAttributes int } func (s Signal) HasOptionalAttribute(attrs map[AttributeName]Attribute) bool { diff --git a/cmd/mdatagen/internal/metric.go b/cmd/mdatagen/internal/metric.go index feb40872b28..c26d393bfb9 100644 --- a/cmd/mdatagen/internal/metric.go +++ b/cmd/mdatagen/internal/metric.go @@ -91,7 +91,15 @@ func (m *Metric) Unmarshal(parser *confmap.Conf) error { if !parser.IsSet("enabled") { return errors.New("missing required field: `enabled`") } - return parser.Unmarshal(m) + + err := parser.Unmarshal(m) + if err != nil { + return err + } + + m.NumAttributes = len(m.Attributes) + + return nil } func (m Metric) Data() MetricData { 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..1546680203e 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,49 @@ 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: AggregationStrategySum, + }, + DefaultMetricToBeRemoved: MetricConfig{ + Enabled: true, + AggregationStrategy: AggregationStrategySum, + }, + MetricInputType: MetricConfig{ + Enabled: true, + AggregationStrategy: AggregationStrategySum, + }, + OptionalMetric: MetricConfig{ + Enabled: true, + AggregationStrategy: AggregationStrategyAvg, + }, + OptionalMetricEmptyUnit: MetricConfig{ + Enabled: true, + AggregationStrategy: AggregationStrategyAvg, + }, + }, + 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 +87,49 @@ 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: AggregationStrategySum, + }, + DefaultMetricToBeRemoved: MetricConfig{ + Enabled: false, + AggregationStrategy: AggregationStrategySum, + }, + MetricInputType: MetricConfig{ + Enabled: false, + AggregationStrategy: AggregationStrategySum, + }, + OptionalMetric: MetricConfig{ + Enabled: false, + AggregationStrategy: AggregationStrategyAvg, + }, + OptionalMetricEmptyUnit: MetricConfig{ + Enabled: false, + AggregationStrategy: AggregationStrategyAvg, + }, + }, + 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 +163,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..12ea97b8f90 100644 --- a/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_metrics.go +++ b/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_metrics.go @@ -16,6 +16,13 @@ import ( "go.opentelemetry.io/collector/pdata/pmetric" ) +const ( + AggregationStrategySum = "sum" + AggregationStrategyAvg = "avg" + AggregationStrategyMin = "min" + AggregationStrategyMax = "max" +) + // AttributeEnumAttr specifies the value enum_attr attribute. type AttributeEnumAttr int @@ -77,13 +84,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 +107,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 +166,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 +187,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 +209,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 +252,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 +273,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 +296,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 +355,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 +376,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 +397,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 +450,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 +471,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 +492,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 +542,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 +623,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), } 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..61f259c63d5 100644 --- a/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_metrics_test.go +++ b/cmd/mdatagen/internal/sampleconnector/internal/metadata/generated_metrics_test.go @@ -43,6 +43,24 @@ func TestMetricsBuilder(t *testing.T) { resAttrsSet: testDataSetNone, expectEmpty: true, }, + { + name: "none_set_max", + metricsSet: testDataSetNone, + resAttrsSet: testDataSetNone, + expectEmpty: true, + }, + { + name: "none_set_min", + metricsSet: testDataSetNone, + resAttrsSet: testDataSetNone, + expectEmpty: true, + }, + { + name: "none_set_avg", + metricsSet: testDataSetNone, + resAttrsSet: testDataSetNone, + expectEmpty: true, + }, { name: "filter_set_include", resAttrsSet: testDataSetAll, @@ -101,6 +119,9 @@ func TestMetricsBuilder(t *testing.T) { 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"}) + // record a second point + mb.RecordDefaultMetricDataPoint(ts, 3, "string_attr-val-2", 20, AttributeEnumAttrGreen, []any{"slice_attr-item3", "slice_attr-item4"}, map[string]any{"key3": "map_attr-val3", "key4": "map_attr-val4"}) + defaultMetricsCount++ allMetricsCount++ mb.RecordDefaultMetricToBeRemovedDataPoint(ts, 1) @@ -109,12 +130,21 @@ func TestMetricsBuilder(t *testing.T) { 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"}) + // record a second point + mb.RecordMetricInputTypeDataPoint(ts, "3", "string_attr-val-2", 20, AttributeEnumAttrGreen, []any{"slice_attr-item3", "slice_attr-item4"}, map[string]any{"key3": "map_attr-val3", "key4": "map_attr-val4"}) + allMetricsCount++ mb.RecordOptionalMetricDataPoint(ts, 1, "string_attr-val", true, false) + // record a second point + mb.RecordOptionalMetricDataPoint(ts, 3, "string_attr-val-2", false, true) + allMetricsCount++ mb.RecordOptionalMetricEmptyUnitDataPoint(ts, 1, "string_attr-val", true) + // record a second point + mb.RecordOptionalMetricEmptyUnitDataPoint(ts, 3, "string_attr-val-2", false) + rb := mb.NewResourceBuilder() rb.SetMapResourceAttr(map[string]any{"key1": "map.resource.attr-val1", "key2": "map.resource.attr-val2"}) rb.SetOptionalResourceAttr("optional.resource.attr-val") @@ -150,113 +180,248 @@ 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) + switch tt.name { + case "all_set": + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + case "none_set_min": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + case "none_set_max": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(3), dp.IntValue()) + case "none_set_avg": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(2), dp.IntValue()) + default: + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), 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()) - assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + dp := ms.At(i).Sum().DataPoints().At(0) + switch tt.name { + case "all_set": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_min": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_max": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.InDelta(t, float64(3), dp.DoubleValue(), 0.01) + case "none_set_avg": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.InDelta(t, float64(2), dp.DoubleValue(), 0.01) + default: + 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) + switch tt.name { + case "all_set": + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + case "none_set_min": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + case "none_set_max": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(3), dp.IntValue()) + case "none_set_avg": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(2), dp.IntValue()) + default: + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), 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) + switch tt.name { + case "all_set": + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_min": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_max": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(3), dp.DoubleValue(), 0.01) + case "none_set_avg": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(2), dp.DoubleValue(), 0.01) + default: + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), 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) + switch tt.name { + case "all_set": + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_min": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_max": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(3), dp.DoubleValue(), 0.01) + case "none_set_avg": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(2), dp.DoubleValue(), 0.01) + default: + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), 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..84115d50f7c 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,156 @@ 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 + default.metric.to_be_removed: + enabled: false + metric.input_type: + enabled: false + optional.metric: + enabled: false + optional.metric.empty_unit: + enabled: false + resource_attributes: + map.resource.attr: + enabled: false + optional.resource.attr: + enabled: false + slice.resource.attr: + enabled: false + string.enum.resource.attr: + enabled: false + string.resource.attr: + enabled: false + string.resource.attr_disable_warning: + enabled: false + string.resource.attr_remove_warning: + enabled: false + string.resource.attr_to_be_removed: + enabled: false +none_set_min: + 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 + aggregation_strategy: min + metrics: + default.metric: + enabled: false + default.metric.to_be_removed: + enabled: false + metric.input_type: + enabled: false + optional.metric: + enabled: false + optional.metric.empty_unit: + enabled: false + resource_attributes: + map.resource.attr: + enabled: false + optional.resource.attr: + enabled: false + slice.resource.attr: + enabled: false + string.enum.resource.attr: + enabled: false + string.resource.attr: + enabled: false + string.resource.attr_disable_warning: + enabled: false + string.resource.attr_remove_warning: + enabled: false + string.resource.attr_to_be_removed: + enabled: false +none_set_max: + 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 + aggregation_strategy: max + metrics: + default.metric: + enabled: false + default.metric.to_be_removed: + enabled: false + metric.input_type: + enabled: false + optional.metric: + enabled: false + optional.metric.empty_unit: + enabled: false + resource_attributes: + map.resource.attr: + enabled: false + optional.resource.attr: + enabled: false + slice.resource.attr: + enabled: false + string.enum.resource.attr: + enabled: false + string.resource.attr: + enabled: false + string.resource.attr_disable_warning: + enabled: false + string.resource.attr_remove_warning: + enabled: false + string.resource.attr_to_be_removed: + enabled: false +none_set_avg: + 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 + aggregation_strategy: avg 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/sampleprocessor/internal/metadata/testdata/config.yaml b/cmd/mdatagen/internal/sampleprocessor/internal/metadata/testdata/config.yaml index bcc1a519f60..96a69556564 100644 --- a/cmd/mdatagen/internal/sampleprocessor/internal/metadata/testdata/config.yaml +++ b/cmd/mdatagen/internal/sampleprocessor/internal/metadata/testdata/config.yaml @@ -35,3 +35,57 @@ none_set: enabled: false string.resource.attr_to_be_removed: enabled: false +none_set_min: + resource_attributes: + map.resource.attr: + enabled: false + optional.resource.attr: + enabled: false + slice.resource.attr: + enabled: false + string.enum.resource.attr: + enabled: false + string.resource.attr: + enabled: false + string.resource.attr_disable_warning: + enabled: false + string.resource.attr_remove_warning: + enabled: false + string.resource.attr_to_be_removed: + enabled: false +none_set_max: + resource_attributes: + map.resource.attr: + enabled: false + optional.resource.attr: + enabled: false + slice.resource.attr: + enabled: false + string.enum.resource.attr: + enabled: false + string.resource.attr: + enabled: false + string.resource.attr_disable_warning: + enabled: false + string.resource.attr_remove_warning: + enabled: false + string.resource.attr_to_be_removed: + enabled: false +none_set_avg: + resource_attributes: + map.resource.attr: + enabled: false + optional.resource.attr: + enabled: false + slice.resource.attr: + enabled: false + string.enum.resource.attr: + enabled: false + string.resource.attr: + enabled: false + string.resource.attr_disable_warning: + enabled: false + string.resource.attr_remove_warning: + enabled: false + string.resource.attr_to_be_removed: + enabled: false 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..81fdbe0be2f 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,55 @@ 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: AggregationStrategySum, + }, + DefaultMetricToBeRemoved: MetricConfig{ + Enabled: true, + AggregationStrategy: AggregationStrategySum, + }, + MetricInputType: MetricConfig{ + Enabled: true, + AggregationStrategy: AggregationStrategySum, + }, + OptionalMetric: MetricConfig{ + Enabled: true, + AggregationStrategy: AggregationStrategyAvg, + }, + OptionalMetricEmptyUnit: MetricConfig{ + Enabled: true, + AggregationStrategy: AggregationStrategyAvg, + }, + }, + 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 +93,55 @@ 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: AggregationStrategySum, + }, + DefaultMetricToBeRemoved: MetricConfig{ + Enabled: false, + AggregationStrategy: AggregationStrategySum, + }, + MetricInputType: MetricConfig{ + Enabled: false, + AggregationStrategy: AggregationStrategySum, + }, + OptionalMetric: MetricConfig{ + Enabled: false, + AggregationStrategy: AggregationStrategyAvg, + }, + OptionalMetricEmptyUnit: MetricConfig{ + Enabled: false, + AggregationStrategy: AggregationStrategyAvg, + }, + }, + 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 +185,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..495b0f6a40b 100644 --- a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics.go +++ b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics.go @@ -16,6 +16,13 @@ import ( "go.opentelemetry.io/collector/receiver" ) +const ( + AggregationStrategySum = "sum" + AggregationStrategyAvg = "avg" + AggregationStrategyMin = "min" + AggregationStrategyMax = "max" +) + // AttributeEnumAttr specifies the value enum_attr attribute. type AttributeEnumAttr int @@ -99,13 +106,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 +129,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) - 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) + + 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) + } for _, op := range options { op.apply(dp) } + + 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) + 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 +191,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 +212,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 +234,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 +277,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 +298,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 +321,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 +380,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 +401,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 +422,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) - dp.SetDoubleValue(val) - dp.Attributes().PutStr("string_attr", stringAttrAttributeValue) - dp.Attributes().PutBool("boolean_attr", booleanAttrAttributeValue) - dp.Attributes().PutBool("boolean_attr2", booleanAttr2AttributeValue) + + 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) + } for _, op := range options { op.apply(dp) } + + 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) + 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 +478,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 +499,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 +520,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 +570,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 +651,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), } 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..bf43876f1cd 100644 --- a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics_test.go +++ b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics_test.go @@ -43,6 +43,24 @@ func TestMetricsBuilder(t *testing.T) { resAttrsSet: testDataSetNone, expectEmpty: true, }, + { + name: "none_set_max", + metricsSet: testDataSetNone, + resAttrsSet: testDataSetNone, + expectEmpty: true, + }, + { + name: "none_set_min", + metricsSet: testDataSetNone, + resAttrsSet: testDataSetNone, + expectEmpty: true, + }, + { + name: "none_set_avg", + metricsSet: testDataSetNone, + resAttrsSet: testDataSetNone, + expectEmpty: true, + }, { name: "filter_set_include", resAttrsSet: testDataSetAll, @@ -101,6 +119,9 @@ func TestMetricsBuilder(t *testing.T) { 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")) + // record a second point + mb.RecordDefaultMetricDataPoint(ts, 3, "string_attr-val-2", 20, AttributeEnumAttrGreen, []any{"slice_attr-item3", "slice_attr-item4"}, map[string]any{"key3": "map_attr-val3", "key4": "map_attr-val4"}, WithOptionalIntAttrMetricAttribute(18), WithOptionalStringAttrMetricAttribute("optional_string_attr-val-2")) + defaultMetricsCount++ allMetricsCount++ mb.RecordDefaultMetricToBeRemovedDataPoint(ts, 1) @@ -109,12 +130,21 @@ func TestMetricsBuilder(t *testing.T) { 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"}) + // record a second point + mb.RecordMetricInputTypeDataPoint(ts, "3", "string_attr-val-2", 20, AttributeEnumAttrGreen, []any{"slice_attr-item3", "slice_attr-item4"}, map[string]any{"key3": "map_attr-val3", "key4": "map_attr-val4"}) + allMetricsCount++ mb.RecordOptionalMetricDataPoint(ts, 1, "string_attr-val", true, false, WithOptionalStringAttrMetricAttribute("optional_string_attr-val")) + // record a second point + mb.RecordOptionalMetricDataPoint(ts, 3, "string_attr-val-2", false, true, WithOptionalStringAttrMetricAttribute("optional_string_attr-val-2")) + allMetricsCount++ mb.RecordOptionalMetricEmptyUnitDataPoint(ts, 1, "string_attr-val", true) + // record a second point + mb.RecordOptionalMetricEmptyUnitDataPoint(ts, 3, "string_attr-val-2", false) + rb := mb.NewResourceBuilder() rb.SetMapResourceAttr(map[string]any{"key1": "map.resource.attr-val1", "key2": "map.resource.attr-val2"}) rb.SetOptionalResourceAttr("optional.resource.attr-val") @@ -150,122 +180,269 @@ 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) + switch tt.name { + case "all_set": + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + case "none_set_min": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + case "none_set_max": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(3), dp.IntValue()) + case "none_set_avg": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(2), dp.IntValue()) + default: + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), 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()) - assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + dp := ms.At(i).Sum().DataPoints().At(0) + switch tt.name { + case "all_set": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_min": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_max": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.InDelta(t, float64(3), dp.DoubleValue(), 0.01) + case "none_set_avg": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.InDelta(t, float64(2), dp.DoubleValue(), 0.01) + default: + 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) + switch tt.name { + case "all_set": + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + case "none_set_min": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + case "none_set_max": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(3), dp.IntValue()) + case "none_set_avg": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(2), dp.IntValue()) + default: + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), 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) + switch tt.name { + case "all_set": + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_min": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_max": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(3), dp.DoubleValue(), 0.01) + case "none_set_avg": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(2), dp.DoubleValue(), 0.01) + default: + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), 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) + switch tt.name { + case "all_set": + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_min": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_max": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(3), dp.DoubleValue(), 0.01) + case "none_set_avg": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(2), dp.DoubleValue(), 0.01) + default: + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), 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..3cc182ebe3e 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,193 @@ 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 + default.metric.to_be_removed: + enabled: false + metric.input_type: + enabled: false + optional.metric: + enabled: false + optional.metric.empty_unit: + enabled: false + events: + default.event: + enabled: false + default.event.to_be_removed: + enabled: false + default.event.to_be_renamed: + enabled: false + resource_attributes: + map.resource.attr: + enabled: false + optional.resource.attr: + enabled: false + slice.resource.attr: + enabled: false + string.enum.resource.attr: + enabled: false + string.resource.attr: + enabled: false + string.resource.attr_disable_warning: + enabled: false + string.resource.attr_remove_warning: + enabled: false + string.resource.attr_to_be_removed: + enabled: false +none_set_min: + 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 + aggregation_strategy: min + metrics: + default.metric: + enabled: false + default.metric.to_be_removed: + enabled: false + metric.input_type: + enabled: false + optional.metric: + enabled: false + optional.metric.empty_unit: + enabled: false + events: + default.event: + enabled: false + default.event.to_be_removed: + enabled: false + default.event.to_be_renamed: + enabled: false + resource_attributes: + map.resource.attr: + enabled: false + optional.resource.attr: + enabled: false + slice.resource.attr: + enabled: false + string.enum.resource.attr: + enabled: false + string.resource.attr: + enabled: false + string.resource.attr_disable_warning: + enabled: false + string.resource.attr_remove_warning: + enabled: false + string.resource.attr_to_be_removed: + enabled: false +none_set_max: + 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 + aggregation_strategy: max + metrics: + default.metric: + enabled: false + default.metric.to_be_removed: + enabled: false + metric.input_type: + enabled: false + optional.metric: + enabled: false + optional.metric.empty_unit: + enabled: false + events: + default.event: + enabled: false + default.event.to_be_removed: + enabled: false + default.event.to_be_renamed: + enabled: false + resource_attributes: + map.resource.attr: + enabled: false + optional.resource.attr: + enabled: false + slice.resource.attr: + enabled: false + string.enum.resource.attr: + enabled: false + string.resource.attr: + enabled: false + string.resource.attr_disable_warning: + enabled: false + string.resource.attr_remove_warning: + enabled: false + string.resource.attr_to_be_removed: + enabled: false +none_set_avg: + 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 + aggregation_strategy: avg 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..1546680203e 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,49 @@ 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: AggregationStrategySum, + }, + DefaultMetricToBeRemoved: MetricConfig{ + Enabled: true, + AggregationStrategy: AggregationStrategySum, + }, + MetricInputType: MetricConfig{ + Enabled: true, + AggregationStrategy: AggregationStrategySum, + }, + OptionalMetric: MetricConfig{ + Enabled: true, + AggregationStrategy: AggregationStrategyAvg, + }, + OptionalMetricEmptyUnit: MetricConfig{ + Enabled: true, + AggregationStrategy: AggregationStrategyAvg, + }, + }, + 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 +87,49 @@ 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: AggregationStrategySum, + }, + DefaultMetricToBeRemoved: MetricConfig{ + Enabled: false, + AggregationStrategy: AggregationStrategySum, + }, + MetricInputType: MetricConfig{ + Enabled: false, + AggregationStrategy: AggregationStrategySum, + }, + OptionalMetric: MetricConfig{ + Enabled: false, + AggregationStrategy: AggregationStrategyAvg, + }, + OptionalMetricEmptyUnit: MetricConfig{ + Enabled: false, + AggregationStrategy: AggregationStrategyAvg, + }, + }, + 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 +163,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..7e1bb2618ba 100644 --- a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics.go +++ b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics.go @@ -16,6 +16,13 @@ import ( "go.opentelemetry.io/collector/scraper" ) +const ( + AggregationStrategySum = "sum" + AggregationStrategyAvg = "avg" + AggregationStrategyMin = "min" + AggregationStrategyMax = "max" +) + // AttributeEnumAttr specifies the value enum_attr attribute. type AttributeEnumAttr int @@ -77,13 +84,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 +107,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 +166,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 +187,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 +209,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 +252,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 +273,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 +296,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 +355,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 +376,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 +397,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 +450,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 +471,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 +492,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 +542,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 +623,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), } 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..858f2caab23 100644 --- a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics_test.go +++ b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics_test.go @@ -43,6 +43,24 @@ func TestMetricsBuilder(t *testing.T) { resAttrsSet: testDataSetNone, expectEmpty: true, }, + { + name: "none_set_max", + metricsSet: testDataSetNone, + resAttrsSet: testDataSetNone, + expectEmpty: true, + }, + { + name: "none_set_min", + metricsSet: testDataSetNone, + resAttrsSet: testDataSetNone, + expectEmpty: true, + }, + { + name: "none_set_avg", + metricsSet: testDataSetNone, + resAttrsSet: testDataSetNone, + expectEmpty: true, + }, { name: "filter_set_include", resAttrsSet: testDataSetAll, @@ -101,6 +119,9 @@ func TestMetricsBuilder(t *testing.T) { 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"}) + // record a second point + mb.RecordDefaultMetricDataPoint(ts, 3, "string_attr-val-2", 20, AttributeEnumAttrGreen, []any{"slice_attr-item3", "slice_attr-item4"}, map[string]any{"key3": "map_attr-val3", "key4": "map_attr-val4"}) + defaultMetricsCount++ allMetricsCount++ mb.RecordDefaultMetricToBeRemovedDataPoint(ts, 1) @@ -109,12 +130,21 @@ func TestMetricsBuilder(t *testing.T) { 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"}) + // record a second point + mb.RecordMetricInputTypeDataPoint(ts, "3", "string_attr-val-2", 20, AttributeEnumAttrGreen, []any{"slice_attr-item3", "slice_attr-item4"}, map[string]any{"key3": "map_attr-val3", "key4": "map_attr-val4"}) + allMetricsCount++ mb.RecordOptionalMetricDataPoint(ts, 1, "string_attr-val", true, false) + // record a second point + mb.RecordOptionalMetricDataPoint(ts, 3, "string_attr-val-2", false, true) + allMetricsCount++ mb.RecordOptionalMetricEmptyUnitDataPoint(ts, 1, "string_attr-val", true) + // record a second point + mb.RecordOptionalMetricEmptyUnitDataPoint(ts, 3, "string_attr-val-2", false) + rb := mb.NewResourceBuilder() rb.SetMapResourceAttr(map[string]any{"key1": "map.resource.attr-val1", "key2": "map.resource.attr-val2"}) rb.SetOptionalResourceAttr("optional.resource.attr-val") @@ -150,113 +180,248 @@ 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) + switch tt.name { + case "all_set": + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + case "none_set_min": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + case "none_set_max": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(3), dp.IntValue()) + case "none_set_avg": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(2), dp.IntValue()) + default: + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), 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()) - assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + dp := ms.At(i).Sum().DataPoints().At(0) + switch tt.name { + case "all_set": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_min": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_max": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.InDelta(t, float64(3), dp.DoubleValue(), 0.01) + case "none_set_avg": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.InDelta(t, float64(2), dp.DoubleValue(), 0.01) + default: + 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) + switch tt.name { + case "all_set": + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + case "none_set_min": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), dp.IntValue()) + case "none_set_max": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(3), dp.IntValue()) + case "none_set_avg": + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(2), dp.IntValue()) + default: + assert.Equal(t, 2, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, int64(1), 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) + switch tt.name { + case "all_set": + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_min": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_max": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(3), dp.DoubleValue(), 0.01) + case "none_set_avg": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(2), dp.DoubleValue(), 0.01) + default: + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), 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) + switch tt.name { + case "all_set": + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_min": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "none_set_max": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(3), dp.DoubleValue(), 0.01) + case "none_set_avg": + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(2), dp.DoubleValue(), 0.01) + default: + assert.Equal(t, 2, ms.At(i).Gauge().DataPoints().Len()) + assert.InDelta(t, float64(1), 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..84115d50f7c 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,156 @@ 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 + default.metric.to_be_removed: + enabled: false + metric.input_type: + enabled: false + optional.metric: + enabled: false + optional.metric.empty_unit: + enabled: false + resource_attributes: + map.resource.attr: + enabled: false + optional.resource.attr: + enabled: false + slice.resource.attr: + enabled: false + string.enum.resource.attr: + enabled: false + string.resource.attr: + enabled: false + string.resource.attr_disable_warning: + enabled: false + string.resource.attr_remove_warning: + enabled: false + string.resource.attr_to_be_removed: + enabled: false +none_set_min: + 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 + aggregation_strategy: min + metrics: + default.metric: + enabled: false + default.metric.to_be_removed: + enabled: false + metric.input_type: + enabled: false + optional.metric: + enabled: false + optional.metric.empty_unit: + enabled: false + resource_attributes: + map.resource.attr: + enabled: false + optional.resource.attr: + enabled: false + slice.resource.attr: + enabled: false + string.enum.resource.attr: + enabled: false + string.resource.attr: + enabled: false + string.resource.attr_disable_warning: + enabled: false + string.resource.attr_remove_warning: + enabled: false + string.resource.attr_to_be_removed: + enabled: false +none_set_max: + 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 + aggregation_strategy: max + metrics: + default.metric: + enabled: false + default.metric.to_be_removed: + enabled: false + metric.input_type: + enabled: false + optional.metric: + enabled: false + optional.metric.empty_unit: + enabled: false + resource_attributes: + map.resource.attr: + enabled: false + optional.resource.attr: + enabled: false + slice.resource.attr: + enabled: false + string.enum.resource.attr: + enabled: false + string.resource.attr: + enabled: false + string.resource.attr_disable_warning: + enabled: false + string.resource.attr_remove_warning: + enabled: false + string.resource.attr_to_be_removed: + enabled: false +none_set_avg: + 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 + aggregation_strategy: avg metrics: default.metric: enabled: false diff --git a/cmd/mdatagen/internal/samplescraper/metadata.yaml b/cmd/mdatagen/internal/samplescraper/metadata.yaml index efb1a963bb9..09a1e238db5 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..6e77ff8f40c 100644 --- a/cmd/mdatagen/internal/templates/config_test.go.tmpl +++ b/cmd/mdatagen/internal/templates/config_test.go.tmpl @@ -28,10 +28,24 @@ 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: AggregationStrategySum, + {{- else }} + AggregationStrategy: AggregationStrategyAvg, + {{- end }} + }, {{- end }} }, + Attributes: AttributesConfig{ + {{- range $name, $_ := .Attributes }} + {{ $name.Render }}: AttributeConfig{ + Enabled: true, + }, + {{- end }} + }, {{- if .ResourceAttributes }} ResourceAttributes: ResourceAttributesConfig{ {{- range $name, $_ := .ResourceAttributes }} @@ -45,10 +59,24 @@ 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: AggregationStrategySum, + {{- else }} + AggregationStrategy: AggregationStrategyAvg, + {{- end }} + }, {{- end }} }, + Attributes: AttributesConfig{ + {{- range $name, $_ := .Attributes }} + {{ $name.Render }}: AttributeConfig{ + Enabled: false, + }, + {{- end }} + }, {{- if .ResourceAttributes }} ResourceAttributes: ResourceAttributesConfig{ {{- range $name, $_ := .ResourceAttributes }} @@ -92,6 +120,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..8dfd12f87d6 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 and (gt (len (attributeInfo .).Enum) 1) (attributeInfo .).Enum }}Attribute{{ .Render }}{{ (index (attributeInfo .).Enum 1) | 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..532ad03a6d9 100644 --- a/cmd/mdatagen/internal/templates/metrics.go.tmpl +++ b/cmd/mdatagen/internal/templates/metrics.go.tmpl @@ -23,6 +23,13 @@ import ( {{- end }} ) +const ( + AggregationStrategySum = "sum" + AggregationStrategyAvg = "avg" + AggregationStrategyMin = "min" + AggregationStrategyMax = "max" +) + {{ range $name, $info := .Attributes }} {{- if $info.Enum -}} // Attribute{{ $name.Render }} specifies the value {{ $name }} attribute. @@ -96,13 +103,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 +130,51 @@ 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 }} + {{- if $metric.HasOptionalAttribute $.Attributes }} for _, op := range options { op.apply(dp) } - {{- end }} + {{- end }} + + 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) + 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 +187,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 +286,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), diff --git a/cmd/mdatagen/internal/templates/metrics_test.go.tmpl b/cmd/mdatagen/internal/templates/metrics_test.go.tmpl index d4ec81f66ec..a1e39a41006 100644 --- a/cmd/mdatagen/internal/templates/metrics_test.go.tmpl +++ b/cmd/mdatagen/internal/templates/metrics_test.go.tmpl @@ -45,6 +45,24 @@ func TestMetricsBuilder(t *testing.T) { resAttrsSet: testDataSetNone, expectEmpty: true, }, + { + name: "none_set_max", + metricsSet: testDataSetNone, + resAttrsSet: testDataSetNone, + expectEmpty: true, + }, + { + name: "none_set_min", + metricsSet: testDataSetNone, + resAttrsSet: testDataSetNone, + expectEmpty: true, + }, + { + name: "none_set_avg", + metricsSet: testDataSetNone, + resAttrsSet: testDataSetNone, + expectEmpty: true, + }, {{- if .ResourceAttributes }} { name: "filter_set_include", @@ -129,9 +147,25 @@ func TestMetricsBuilder(t *testing.T) { {{- if (attributeInfo .).Optional -}} , With{{ .Render }}MetricAttribute({{- template "getAttributeValue" . -}}) {{- end -}} + {{- end -}} + ) + + {{ if gt $metric.NumAttributes 0 }} + // record a second point + mb.Record{{ $name.Render }}DataPoint(ts, {{ if $metric.Data.HasMetricInputType }}"3"{{ else }}3{{ end }} + {{- range $metric.Attributes -}} + {{- if not (attributeInfo .).Optional -}} + , {{- template "getAttributeValueTwo" . -}} + {{- end -}} + {{- end -}} + {{- range $metric.Attributes -}} + {{- if (attributeInfo .).Optional -}} + , With{{ .Render }}MetricAttribute({{- template "getAttributeValueTwo" . -}}) {{- end -}} + {{- end -}} ) {{- end }} + {{- end }} {{ if .ResourceAttributes }} rb := mb.NewResourceBuilder() @@ -172,7 +206,52 @@ 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()) - assert.Equal(t, 1, ms.At(i).{{ $metric.Data.Type }}().DataPoints().Len()) + dp := ms.At(i).{{ $metric.Data.Type }}().DataPoints().At(0) + switch tt.name { + case "all_set": + {{- if eq $metric.NumAttributes 0 }} + assert.Equal(t, 1, ms.At(i).{{ $metric.Data.Type }}().DataPoints().Len()) + {{- else }} + assert.Equal(t, 2, ms.At(i).{{ $metric.Data.Type }}().DataPoints().Len()) + {{- end }} + {{- 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 }} + case "none_set_min": + 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 }} + case "none_set_max": + 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 }}(3), dp.{{ $metric.Data.MetricValueType }}Value(), 0.01) + {{- else }} + assert.Equal(t, {{ $metric.Data.MetricValueType.BasicType }}(3), dp.{{ $metric.Data.MetricValueType }}Value()) + {{- end }} + case "none_set_avg": + 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 }}(2), dp.{{ $metric.Data.MetricValueType }}Value(), 0.01) + {{- else }} + assert.Equal(t, {{ $metric.Data.MetricValueType.BasicType }}(2), dp.{{ $metric.Data.MetricValueType }}Value()) + {{- end }} + default: + {{- if eq $metric.NumAttributes 0 }} + assert.Equal(t, 1, ms.At(i).{{ $metric.Data.Type }}().DataPoints().Len()) + {{- else }} + assert.Equal(t, 2, ms.At(i).{{ $metric.Data.Type }}().DataPoints().Len()) + {{- end }} + {{- 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 }} + } assert.Equal(t, "{{ $metric.Description }}", ms.At(i).Description()) {{- if len $metric.Unit}} assert.Equal(t, "{{ $metric.Unit }}", ms.At(i).Unit()) @@ -185,27 +264,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..f7fe1eb81f3 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,103 @@ all_set: {{- end }} {{- end }} none_set: + {{- if .Attributes }} + attributes: + {{- range $name, $_ := .Attributes }} + {{ $name }}: + enabled: false + {{- end }} + {{- end }} + {{- if .Metrics }} + metrics: + {{- range $name, $_ := .Metrics }} + {{ $name }}: + enabled: false + {{- end }} + {{- end }} + {{- if .Events }} + events: + {{- range $name, $_ := .Events }} + {{ $name }}: + enabled: false + {{- end }} + {{- end }} + {{- if .ResourceAttributes }} + resource_attributes: + {{- range $name, $_ := .ResourceAttributes }} + {{ $name }}: + enabled: false + {{- end }} + {{- end }} +none_set_min: + {{- if .Attributes }} + attributes: + {{- range $name, $_ := .Attributes }} + {{ $name }}: + enabled: false + {{- end }} + aggregation_strategy: min + {{- end }} + {{- if .Metrics }} + metrics: + {{- range $name, $_ := .Metrics }} + {{ $name }}: + enabled: false + {{- end }} + {{- end }} + {{- if .Events }} + events: + {{- range $name, $_ := .Events }} + {{ $name }}: + enabled: false + {{- end }} + {{- end }} + {{- if .ResourceAttributes }} + resource_attributes: + {{- range $name, $_ := .ResourceAttributes }} + {{ $name }}: + enabled: false + {{- end }} + {{- end }} +none_set_max: + {{- if .Attributes }} + attributes: + {{- range $name, $_ := .Attributes }} + {{ $name }}: + enabled: false + {{- end }} + aggregation_strategy: max + {{- end }} + {{- if .Metrics }} + metrics: + {{- range $name, $_ := .Metrics }} + {{ $name }}: + enabled: false + {{- end }} + {{- end }} + {{- if .Events }} + events: + {{- range $name, $_ := .Events }} + {{ $name }}: + enabled: false + {{- end }} + {{- end }} + {{- if .ResourceAttributes }} + resource_attributes: + {{- range $name, $_ := .ResourceAttributes }} + {{ $name }}: + enabled: false + {{- end }} + {{- end }} +none_set_avg: + {{- if .Attributes }} + attributes: + {{- range $name, $_ := .Attributes }} + {{ $name }}: + enabled: false + {{- end }} + aggregation_strategy: avg + {{- 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 353cdb4c20b..e172808a9d0 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: + # Optional: 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. @@ -124,6 +127,10 @@ metrics: level: # Optional: the version current stability was introduced from: + # 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.