diff --git a/CHANGELOG.md b/CHANGELOG.md index 138cee423aca..0a52225b63ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### 🛑 Breaking changes 🛑 + +- `windowsperfcountersreceiver`: Added metrics configuration (#8376) + ## v0.47.0 ### 💡 Enhancements 💡 diff --git a/receiver/windowsperfcountersreceiver/README.md b/receiver/windowsperfcountersreceiver/README.md index 9d7c4216ecb9..77e95b4d62a4 100644 --- a/receiver/windowsperfcountersreceiver/README.md +++ b/receiver/windowsperfcountersreceiver/README.md @@ -6,9 +6,6 @@ interface](https://docs.microsoft.com/en-us/windows/win32/perfctrs/using-the-pdh It is based on the [Telegraf Windows Performance Counters Input Plugin](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/win_perf_counters). -Metrics will be generated with names and labels that match the performance -counter path, i.e. - - `Memory\Committed Bytes` - `Processor\% Processor Time`, with a datapoint for each `Instance` label = (`_Total`, `1`, `2`, `3`, ... ) @@ -25,11 +22,25 @@ be configured: ```yaml windowsperfcounters: collection_interval: # default = "1m" + metrics: + : + description: + unit: + gauge: + : + description: + unit: + sum: + aggregation: + monotonic: perfcounters: - object: instances: []* counters: - - + - name: + metric: + attributes: + : ``` *Note `instances` can have several special values depending on the type of @@ -53,23 +64,40 @@ you can configure multiple `windowsperfcounters` receivers with different ```yaml receivers: windowsperfcounters/memory: + metrics: + bytes.committed: + description: the number of bytes committed to memory + unit: By + gauge: collection_interval: 30s perfcounters: - object: Memory counters: - - Committed Bytes + - name: Committed Bytes + metric: bytes.committed windowsperfcounters/processor: collection_interval: 1m + metrics: + processor.time: + description: active and idle time of the processor + unit: "%" + gauge: perfcounters: - object: "Processor" instances: "*" counters: - - "% Processor Time" + - name: "% Processor Time" + metric: processor.time + attributes: + state: active - object: "Processor" instances: [1, 2] counters: - - "% Idle Time" + - name: "% Idle Time" + metric: processor.time + attributes: + state: idle service: pipelines: @@ -77,36 +105,52 @@ service: receivers: [windowsperfcounters/memory, windowsperfcounters/processor] ``` -### Changing metric format +### Defining metric format + +To report metrics in the desired output format, define a metric and reference it in the corresponding counter, along with any applicable attributes. The metric's data type can either be `gauge` (default) or `sum`. + +| Field Name | Description | Value | Default | +| -- | -- | -- | -- | +| name | The key for the metric. | string | Counter Name | +| description | definition of what the metric measures. | string | | +| unit | what is being measured. | string | `1` | +| sum | representation of a sum metric. | Sum Config | | +| gauge | representation of a gauge metric. | Gauge Config | | + -To report metrics in the desired output format, it's recommended you use this -receiver with the [metrics transform -processor](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/metricstransformprocessor). +#### Sum Config + +| Field Name | Description | Value | Default | +| -- | -- | -- | -- | +| aggregation | The type of aggregation temporality for the metric. | [`cumulative` or `delta`] | | +| monotonic | whether or not the metric value can decrease. | false | | + +#### Gauge Config + +A `gauge` config currently accepts no settings. It is specified as an object for forwards compatibility. e.g. To output the `Memory/Committed Bytes` counter as a metric with the name -`system.memory.usage`: +`bytes.committed`: ```yaml receivers: windowsperfcounters: + metrics: + bytes.committed: + description: the number of bytes committed to memory + unit: By + gauge: collection_interval: 30s perfcounters: - object: Memory counters: - - Committed Bytes - -processors: - metricstransformprocessor: - transforms: - - metric_name: "Memory/Committed Bytes" - action: update - new_name: system.memory.usage + - name: Committed Bytes + metric: bytes.committed service: pipelines: metrics: receivers: [windowsperfcounters] - processors: [metricstransformprocessor] ``` ## Recommended configuration for common applications diff --git a/receiver/windowsperfcountersreceiver/config.go b/receiver/windowsperfcountersreceiver/config.go index 396acb242cc3..f73b356c4c6e 100644 --- a/receiver/windowsperfcountersreceiver/config.go +++ b/receiver/windowsperfcountersreceiver/config.go @@ -25,14 +25,37 @@ import ( type Config struct { scraperhelper.ScraperControllerSettings `mapstructure:",squash"` - PerfCounters []PerfCounterConfig `mapstructure:"perfcounters"` + MetricMetaData map[string]MetricConfig `mapstructure:"metrics"` + PerfCounters []PerfCounterConfig `mapstructure:"perfcounters"` } // PerfCounterConfig defines configuration for a perf counter object. type PerfCounterConfig struct { - Object string `mapstructure:"object"` - Instances []string `mapstructure:"instances"` - Counters []string `mapstructure:"counters"` + Object string `mapstructure:"object"` + Instances []string `mapstructure:"instances"` + Counters []CounterConfig `mapstructure:"counters"` +} + +// MetricsConfig defines the configuration for a metric to be created. +type MetricConfig struct { + Unit string `mapstructure:"unit"` + Description string `mapstructure:"description"` + Gauge GaugeMetric `mapstructure:"gauge"` + Sum SumMetric `mapstructure:"sum"` +} + +type GaugeMetric struct { +} + +type SumMetric struct { + Aggregation string `mapstructure:"aggregation"` + Monotonic bool `mapstructure:"monotonic"` +} + +type CounterConfig struct { + Metric string `mapstructure:"metric"` + Name string `mapstructure:"name"` + Attributes map[string]string `mapstructure:"attributes"` } func (c *Config) Validate() error { @@ -46,6 +69,22 @@ func (c *Config) Validate() error { errs = multierr.Append(errs, fmt.Errorf("must specify at least one perf counter")) } + for name, metric := range c.MetricMetaData { + if metric.Unit == "" { + metric.Unit = "1" + } + + if (metric.Sum != SumMetric{}) { + if (metric.Gauge != GaugeMetric{}) { + errs = multierr.Append(errs, fmt.Errorf("metric %q provides both a sum config and a gauge config", name)) + } + + if metric.Sum.Aggregation != "cumulative" && metric.Sum.Aggregation != "delta" { + errs = multierr.Append(errs, fmt.Errorf("sum metric %q includes an invalid aggregation", name)) + } + } + } + var perfCounterMissingObjectName bool for _, pc := range c.PerfCounters { if pc.Object == "" { @@ -53,16 +92,32 @@ func (c *Config) Validate() error { continue } + if len(pc.Counters) == 0 { + errs = multierr.Append(errs, fmt.Errorf("perf counter for object %q does not specify any counters", pc.Object)) + } + + for _, counter := range pc.Counters { + if counter.Metric == "" { + continue + } + + foundMatchingMetric := false + for name := range c.MetricMetaData { + if counter.Metric == name { + foundMatchingMetric = true + } + } + if !foundMatchingMetric { + errs = multierr.Append(errs, fmt.Errorf("perf counter for object %q includes an undefined metric", pc.Object)) + } + } + for _, instance := range pc.Instances { if instance == "" { errs = multierr.Append(errs, fmt.Errorf("perf counter for object %q includes an empty instance", pc.Object)) break } } - - if len(pc.Counters) == 0 { - errs = multierr.Append(errs, fmt.Errorf("perf counter for object %q does not specify any counters", pc.Object)) - } } if perfCounterMissingObjectName { diff --git a/receiver/windowsperfcountersreceiver/config_test.go b/receiver/windowsperfcountersreceiver/config_test.go index 2f88f381e668..7c7c7a7213d7 100644 --- a/receiver/windowsperfcountersreceiver/config_test.go +++ b/receiver/windowsperfcountersreceiver/config_test.go @@ -43,10 +43,27 @@ func TestLoadConfig(t *testing.T) { r0 := cfg.Receivers[config.NewComponentID(typeStr)] defaultConfigSingleObject := factory.CreateDefaultConfig() - defaultConfigSingleObject.(*Config).PerfCounters = []PerfCounterConfig{{Object: "object", Counters: []string{"counter"}}} + + counterConfig := CounterConfig{ + Name: "counter1", + Metric: "metric", + } + defaultConfigSingleObject.(*Config).PerfCounters = []PerfCounterConfig{{Object: "object", Counters: []CounterConfig{counterConfig}}} + defaultConfigSingleObject.(*Config).MetricMetaData = map[string]MetricConfig{ + "metric": { + Description: "desc", + Unit: "1", + Gauge: GaugeMetric{}, + }, + } assert.Equal(t, defaultConfigSingleObject, r0) + counterConfig2 := CounterConfig{ + Name: "counter2", + Metric: "metric2", + } + r1 := cfg.Receivers[config.NewComponentIDWithName(typeStr, "customname")].(*Config) expectedConfig := &Config{ ScraperControllerSettings: scraperhelper.ScraperControllerSettings{ @@ -56,11 +73,23 @@ func TestLoadConfig(t *testing.T) { PerfCounters: []PerfCounterConfig{ { Object: "object1", - Counters: []string{"counter1"}, + Counters: []CounterConfig{counterConfig}, }, { Object: "object2", - Counters: []string{"counter1", "counter2"}, + Counters: []CounterConfig{counterConfig, counterConfig2}, + }, + }, + MetricMetaData: map[string]MetricConfig{ + "metric": { + Description: "desc", + Unit: "1", + Gauge: GaugeMetric{}, + }, + "metric2": { + Description: "desc", + Unit: "1", + Gauge: GaugeMetric{}, }, }, } @@ -68,6 +97,109 @@ func TestLoadConfig(t *testing.T) { assert.Equal(t, expectedConfig, r1) } +func TestLoadConfigMetrics(t *testing.T) { + testCases := []struct { + TestName string + TestPath string + Expected Config + }{ + { + TestName: "NoMetricsDefined", + TestPath: filepath.Join("testdata", "config-nometrics.yaml"), + Expected: Config{ + PerfCounters: []PerfCounterConfig{ + { + Object: "object", + Counters: []CounterConfig{{Name: "counter1"}}, + }, + }, + }, + }, + { + TestName: "NoMetricSpecified", + TestPath: filepath.Join("testdata", "config-nometricspecified.yaml"), + Expected: Config{ + PerfCounters: []PerfCounterConfig{ + { + Object: "object", + Counters: []CounterConfig{{Name: "counter1"}}, + }, + }, + MetricMetaData: map[string]MetricConfig{ + "metric": { + Description: "desc", + Unit: "1", + Gauge: GaugeMetric{}, + }, + }, + }, + }, + { + TestName: "SumMetric", + TestPath: filepath.Join("testdata", "config-summetric.yaml"), + Expected: Config{ + PerfCounters: []PerfCounterConfig{ + { + Object: "object", + Counters: []CounterConfig{{Name: "counter1", Metric: "metric"}}, + }, + }, + MetricMetaData: map[string]MetricConfig{ + "metric": { + Description: "desc", + Unit: "1", + Sum: SumMetric{ + Aggregation: "cumulative", + Monotonic: false, + }, + }, + }, + }, + }, + { + TestName: "MetricUnspecifiedType", + TestPath: filepath.Join("testdata", "config-unspecifiedmetrictype.yaml"), + Expected: Config{ + PerfCounters: []PerfCounterConfig{ + { + Object: "object", + Counters: []CounterConfig{{Name: "counter1", Metric: "metric"}}, + }, + }, + MetricMetaData: map[string]MetricConfig{ + "metric": { + Description: "desc", + Unit: "1", + Gauge: GaugeMetric{}, + }, + }, + }, + }, + } + for _, test := range testCases { + t.Run(test.TestName, func(t *testing.T) { + factories, err := componenttest.NopFactories() + require.NoError(t, err) + + factory := NewFactory() + factories.Receivers[typeStr] = factory + cfg, err := servicetest.LoadConfigAndValidate(test.TestPath, factories) + + require.NoError(t, err) + require.NotNil(t, cfg) + + assert.Equal(t, len(cfg.Receivers), 1) + + actualReceiver := cfg.Receivers[config.NewComponentID(typeStr)] + expectedReceiver := factory.CreateDefaultConfig() + expectedReceiver.(*Config).PerfCounters = test.Expected.PerfCounters + expectedReceiver.(*Config).MetricMetaData = test.Expected.MetricMetaData + + assert.Equal(t, expectedReceiver, actualReceiver) + }) + } +} + func TestLoadConfig_Error(t *testing.T) { type testCase struct { name string @@ -117,8 +249,8 @@ func TestLoadConfig_Error(t *testing.T) { "%s: %s; %s; %s; %s", errorPrefix, negativeCollectionIntervalErr, - fmt.Sprintf(emptyInstanceErr, "object"), fmt.Sprintf(noCountersErr, "object"), + fmt.Sprintf(emptyInstanceErr, "object"), noObjectNameErr, ), }, diff --git a/receiver/windowsperfcountersreceiver/factory_others_test.go b/receiver/windowsperfcountersreceiver/factory_others_test.go index babbf2069dfd..23254fb323b2 100644 --- a/receiver/windowsperfcountersreceiver/factory_others_test.go +++ b/receiver/windowsperfcountersreceiver/factory_others_test.go @@ -28,8 +28,20 @@ import ( func TestCreateMetricsReceiver(t *testing.T) { factory := NewFactory() cfg := factory.CreateDefaultConfig() - cfg.(*Config).PerfCounters = []PerfCounterConfig{{Object: "object", Counters: []string{"counter"}}} + cfg.(*Config).PerfCounters = []PerfCounterConfig{ + { + Object: "object", + Counters: []CounterConfig{{Name: "counter", Metric: "metric"}}, + }, + } + cfg.(*Config).MetricMetaData = map[string]MetricConfig{ + "metric": { + Description: "desc", + Unit: "1", + Gauge: GaugeMetric{}, + }, + } mReceiver, err := factory.CreateMetricsReceiver(context.Background(), creationParams, cfg, consumertest.NewNop()) assert.EqualError(t, err, "the windows perf counters receiver is only supported on Windows") diff --git a/receiver/windowsperfcountersreceiver/factory_test.go b/receiver/windowsperfcountersreceiver/factory_test.go index 23e9b2d3ad88..5d3101772e0a 100644 --- a/receiver/windowsperfcountersreceiver/factory_test.go +++ b/receiver/windowsperfcountersreceiver/factory_test.go @@ -34,7 +34,20 @@ func TestCreateDefaultConfig(t *testing.T) { assert.NotNil(t, cfg, "failed to create default config") assert.NoError(t, configtest.CheckConfigStruct(cfg)) - cfg.(*Config).PerfCounters = []PerfCounterConfig{{Object: "object", Counters: []string{"counter"}}} + cfg.(*Config).PerfCounters = []PerfCounterConfig{ + { + Object: "object", + Counters: []CounterConfig{{Name: "counter", Metric: "metric"}}, + }, + } + + cfg.(*Config).MetricMetaData = map[string]MetricConfig{ + "metric": { + Description: "desc", + Unit: "1", + Gauge: GaugeMetric{}, + }, + } assert.NoError(t, cfg.Validate()) } @@ -42,8 +55,35 @@ func TestCreateDefaultConfig(t *testing.T) { func TestCreateTracesReceiver(t *testing.T) { factory := NewFactory() cfg := factory.CreateDefaultConfig() - cfg.(*Config).PerfCounters = []PerfCounterConfig{{Object: "object", Counters: []string{"counter"}}} + cfg.(*Config).PerfCounters = []PerfCounterConfig{ + { + Object: "object", + Counters: []CounterConfig{{Name: "counter", Metric: "metric"}}, + }, + } + + cfg.(*Config).MetricMetaData = map[string]MetricConfig{ + "metric": { + Description: "desc", + Unit: "1", + Gauge: GaugeMetric{}, + }, + } + tReceiver, err := factory.CreateTracesReceiver(context.Background(), creationParams, cfg, consumertest.NewNop()) + + assert.ErrorIs(t, err, componenterror.ErrDataTypeIsNotSupported) + assert.Nil(t, tReceiver) +} +func TestCreateTracesReceiverNoMetrics(t *testing.T) { + factory := NewFactory() + cfg := factory.CreateDefaultConfig() + cfg.(*Config).PerfCounters = []PerfCounterConfig{ + { + Object: "object", + Counters: []CounterConfig{{Name: "counter"}}, + }, + } tReceiver, err := factory.CreateTracesReceiver(context.Background(), creationParams, cfg, consumertest.NewNop()) assert.ErrorIs(t, err, componenterror.ErrDataTypeIsNotSupported) @@ -53,7 +93,20 @@ func TestCreateTracesReceiver(t *testing.T) { func TestCreateLogsReceiver(t *testing.T) { factory := NewFactory() cfg := factory.CreateDefaultConfig() - cfg.(*Config).PerfCounters = []PerfCounterConfig{{Object: "object", Counters: []string{"counter"}}} + cfg.(*Config).PerfCounters = []PerfCounterConfig{ + { + Object: "object", + Counters: []CounterConfig{{Name: "counter", Metric: "metric"}}, + }, + } + + cfg.(*Config).MetricMetaData = map[string]MetricConfig{ + "metric": { + Description: "desc", + Unit: "1", + Gauge: GaugeMetric{}, + }, + } tReceiver, err := factory.CreateLogsReceiver(context.Background(), creationParams, cfg, consumertest.NewNop()) diff --git a/receiver/windowsperfcountersreceiver/factory_windows.go b/receiver/windowsperfcountersreceiver/factory_windows.go index fca7bd0e19f9..a7c932b835fb 100644 --- a/receiver/windowsperfcountersreceiver/factory_windows.go +++ b/receiver/windowsperfcountersreceiver/factory_windows.go @@ -34,10 +34,7 @@ func createMetricsReceiver( consumer consumer.Metrics, ) (component.MetricsReceiver, error) { oCfg := cfg.(*Config) - scraper, err := newScraper(oCfg, params.Logger) - if err != nil { - return nil, err - } + scraper := newScraper(oCfg, params.TelemetrySettings) scrp, err := scraperhelper.NewScraper( cfg.ID().String(), diff --git a/receiver/windowsperfcountersreceiver/factory_windows_test.go b/receiver/windowsperfcountersreceiver/factory_windows_test.go index 00c824d21685..d6e1a4bd0edc 100644 --- a/receiver/windowsperfcountersreceiver/factory_windows_test.go +++ b/receiver/windowsperfcountersreceiver/factory_windows_test.go @@ -28,7 +28,20 @@ import ( func TestCreateMetricsReceiver(t *testing.T) { factory := NewFactory() cfg := factory.CreateDefaultConfig() - cfg.(*Config).PerfCounters = []PerfCounterConfig{{Object: "object", Counters: []string{"counter"}}} + cfg.(*Config).PerfCounters = []PerfCounterConfig{ + { + Object: "object", + Counters: []CounterConfig{{Name: "counter", Metric: "metric"}}, + }, + } + + cfg.(*Config).MetricMetaData = map[string]MetricConfig{ + "metric": { + Description: "desc", + Unit: "1", + Gauge: GaugeMetric{}, + }, + } mReceiver, err := factory.CreateMetricsReceiver(context.Background(), creationParams, cfg, consumertest.NewNop()) diff --git a/receiver/windowsperfcountersreceiver/go.mod b/receiver/windowsperfcountersreceiver/go.mod index 5ff7241734fa..223a9a2f5e68 100644 --- a/receiver/windowsperfcountersreceiver/go.mod +++ b/receiver/windowsperfcountersreceiver/go.mod @@ -16,11 +16,11 @@ require ( github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/knadh/koanf v1.4.0 // indirect - github.com/kr/pretty v0.3.0 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/scrapertest v0.47.0 github.com/pelletier/go-toml v1.9.4 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -34,3 +34,8 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) + +require ( + github.com/kr/text v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.6.1 // indirect +) diff --git a/receiver/windowsperfcountersreceiver/go.sum b/receiver/windowsperfcountersreceiver/go.sum index ca928ae35252..ba80e1898974 100644 --- a/receiver/windowsperfcountersreceiver/go.sum +++ b/receiver/windowsperfcountersreceiver/go.sum @@ -104,7 +104,6 @@ github.com/knadh/koanf v1.4.0/go.mod h1:1cfH5223ZeZUOs8FU2UdTmaNfHpqgtjV0+NHjRO4 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -131,6 +130,8 @@ github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/mostynb/go-grpc-compression v1.1.16 h1:D9tGUINmcII049pxOj9dl32Fzhp26TrDVQXECoKJqQg= github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/scrapertest v0.47.0 h1:61MPuKne5R2/w54YZjI/S1Iw+X7N7rsL3CZ/EDEWdDk= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/scrapertest v0.47.0/go.mod h1:lW6MaaOr/PMf2Tno2WcwHJBkEJYsmhcosVqib/JFVI8= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= diff --git a/receiver/windowsperfcountersreceiver/testdata/config-emptyinstance.yaml b/receiver/windowsperfcountersreceiver/testdata/config-emptyinstance.yaml index c39318a307dc..5a8b2091dc67 100644 --- a/receiver/windowsperfcountersreceiver/testdata/config-emptyinstance.yaml +++ b/receiver/windowsperfcountersreceiver/testdata/config-emptyinstance.yaml @@ -1,10 +1,16 @@ receivers: windowsperfcounters: + metrics: + metric: + description: desc + unit: "1" + gauge: perfcounters: - object: "object" instances: [""] counters: - - "counter" + - name: counter + metric: metric processors: nop: diff --git a/receiver/windowsperfcountersreceiver/testdata/config-negative-collection-interval.yaml b/receiver/windowsperfcountersreceiver/testdata/config-negative-collection-interval.yaml index e682561b55bc..83a5c9d25a64 100644 --- a/receiver/windowsperfcountersreceiver/testdata/config-negative-collection-interval.yaml +++ b/receiver/windowsperfcountersreceiver/testdata/config-negative-collection-interval.yaml @@ -1,10 +1,16 @@ receivers: windowsperfcounters: + metrics: + metric: + description: desc + unit: "1" + gauge: collection_interval: -1m perfcounters: - object: "object" counters: - - "counter" + - name: counter1 + metric: metric processors: nop: diff --git a/receiver/windowsperfcountersreceiver/testdata/config-nocounters.yaml b/receiver/windowsperfcountersreceiver/testdata/config-nocounters.yaml index b93aa0e23eee..7781bb162af0 100644 --- a/receiver/windowsperfcountersreceiver/testdata/config-nocounters.yaml +++ b/receiver/windowsperfcountersreceiver/testdata/config-nocounters.yaml @@ -1,5 +1,10 @@ receivers: windowsperfcounters: + metrics: + metric: + description: desc + unit: "1" + gauge: perfcounters: - object: "object" diff --git a/receiver/windowsperfcountersreceiver/testdata/config-nometrics.yaml b/receiver/windowsperfcountersreceiver/testdata/config-nometrics.yaml new file mode 100644 index 000000000000..efa3251595b5 --- /dev/null +++ b/receiver/windowsperfcountersreceiver/testdata/config-nometrics.yaml @@ -0,0 +1,19 @@ +receivers: + windowsperfcounters: + perfcounters: + - object: "object" + counters: + - name: counter1 + +processors: + nop: + +exporters: + nop: + +service: + pipelines: + metrics: + receivers: [windowsperfcounters] + processors: [nop] + exporters: [nop] diff --git a/receiver/windowsperfcountersreceiver/testdata/config-nometricspecified.yaml b/receiver/windowsperfcountersreceiver/testdata/config-nometricspecified.yaml new file mode 100644 index 000000000000..8277abe96d46 --- /dev/null +++ b/receiver/windowsperfcountersreceiver/testdata/config-nometricspecified.yaml @@ -0,0 +1,24 @@ +receivers: + windowsperfcounters: + metrics: + metric: + description: desc + unit: "1" + gauge: + perfcounters: + - object: "object" + counters: + - name: counter1 + +processors: + nop: + +exporters: + nop: + +service: + pipelines: + metrics: + receivers: [windowsperfcounters] + processors: [nop] + exporters: [nop] diff --git a/receiver/windowsperfcountersreceiver/testdata/config-noobjectname.yaml b/receiver/windowsperfcountersreceiver/testdata/config-noobjectname.yaml index 61719356b62f..ad2d27ed10c7 100644 --- a/receiver/windowsperfcountersreceiver/testdata/config-noobjectname.yaml +++ b/receiver/windowsperfcountersreceiver/testdata/config-noobjectname.yaml @@ -1,8 +1,14 @@ receivers: windowsperfcounters: + metrics: + metric: + description: desc + unit: "1" + gauge: perfcounters: - counters: - - "counter" + + processors: nop: diff --git a/receiver/windowsperfcountersreceiver/testdata/config-noperfcounters.yaml b/receiver/windowsperfcountersreceiver/testdata/config-noperfcounters.yaml index ff272819dd27..03d5d4eb3382 100644 --- a/receiver/windowsperfcountersreceiver/testdata/config-noperfcounters.yaml +++ b/receiver/windowsperfcountersreceiver/testdata/config-noperfcounters.yaml @@ -1,6 +1,10 @@ receivers: windowsperfcounters: - + metrics: + metric: + description: desc + unit: "1" + gauge: processors: nop: diff --git a/receiver/windowsperfcountersreceiver/testdata/config-summetric.yaml b/receiver/windowsperfcountersreceiver/testdata/config-summetric.yaml new file mode 100644 index 000000000000..0efdf4af1ebe --- /dev/null +++ b/receiver/windowsperfcountersreceiver/testdata/config-summetric.yaml @@ -0,0 +1,26 @@ +receivers: + windowsperfcounters: + metrics: + metric: + description: desc + unit: "1" + sum: + aggregation: cumulative + perfcounters: + - object: "object" + counters: + - name: counter1 + metric: metric + +processors: + nop: + +exporters: + nop: + +service: + pipelines: + metrics: + receivers: [windowsperfcounters] + processors: [nop] + exporters: [nop] diff --git a/receiver/windowsperfcountersreceiver/testdata/config-unspecifiedmetrictype.yaml b/receiver/windowsperfcountersreceiver/testdata/config-unspecifiedmetrictype.yaml new file mode 100644 index 000000000000..831b395dc24a --- /dev/null +++ b/receiver/windowsperfcountersreceiver/testdata/config-unspecifiedmetrictype.yaml @@ -0,0 +1,24 @@ +receivers: + windowsperfcounters: + metrics: + metric: + description: desc + unit: "1" + perfcounters: + - object: "object" + counters: + - name: counter1 + metric: metric + +processors: + nop: + +exporters: + nop: + +service: + pipelines: + metrics: + receivers: [windowsperfcounters] + processors: [nop] + exporters: [nop] diff --git a/receiver/windowsperfcountersreceiver/testdata/config.yaml b/receiver/windowsperfcountersreceiver/testdata/config.yaml index ab2b288e6325..41660cebd152 100644 --- a/receiver/windowsperfcountersreceiver/testdata/config.yaml +++ b/receiver/windowsperfcountersreceiver/testdata/config.yaml @@ -1,19 +1,37 @@ receivers: windowsperfcounters: + metrics: + metric: + description: desc + unit: "1" + gauge: perfcounters: - object: "object" counters: - - "counter" + - name: counter1 + metric: metric windowsperfcounters/customname: + metrics: + metric: + description: desc + unit: "1" + gauge: + metric2: + description: desc + unit: "1" + gauge: collection_interval: 30s perfcounters: - - object: "object1" + - object: object1 counters: - - "counter1" - - object: "object2" + - name: counter1 + metric: metric + - object: object2 counters: - - "counter1" - - "counter2" + - name: counter1 + metric: metric + - name: counter2 + metric: metric2 processors: nop: diff --git a/receiver/windowsperfcountersreceiver/testdata/scraper/no_metric_def.json b/receiver/windowsperfcountersreceiver/testdata/scraper/no_metric_def.json new file mode 100644 index 000000000000..f90b666ee4e3 --- /dev/null +++ b/receiver/windowsperfcountersreceiver/testdata/scraper/no_metric_def.json @@ -0,0 +1,25 @@ +{ + "resourceMetrics": [ + { + "instrumentationLibraryMetrics": [ + { + "instrumentationLibrary": {}, + "metrics": [ + { + "gauge": { + "dataPoints": [ + { + "asInt": "25089622016", + "timeUnixNano": "1647459021285009300" + } + ] + }, + "name": "\\Memory\\Committed Bytes" + } + ] + } + ], + "resource": {} + } + ] +} diff --git a/receiver/windowsperfcountersreceiver/testdata/scraper/standard.json b/receiver/windowsperfcountersreceiver/testdata/scraper/standard.json new file mode 100644 index 000000000000..f117a87e29cf --- /dev/null +++ b/receiver/windowsperfcountersreceiver/testdata/scraper/standard.json @@ -0,0 +1,165 @@ +{ + "resourceMetrics": [ + { + "instrumentationLibraryMetrics": [ + { + "instrumentationLibrary": {}, + "metrics": [ + { + "description": "percentage of time CPU is idle.", + "gauge": { + "dataPoints": [ + { + "asDouble": 0, + "attributes": [ + { + "key": "instance", + "value": { + "stringValue": "0" + } + } + ], + "timeUnixNano": "1646857199239674900" + }, + { + "asDouble": 0, + "attributes": [ + { + "key": "instance", + "value": { + "stringValue": "1" + } + } + ], + "timeUnixNano": "1646857199239674900" + }, + { + "asDouble": 0, + "attributes": [ + { + "key": "instance", + "value": { + "stringValue": "2" + } + } + ], + "timeUnixNano": "1646857199239674900" + }, + { + "asDouble": 0, + "attributes": [ + { + "key": "instance", + "value": { + "stringValue": "3" + } + } + ], + "timeUnixNano": "1646857199239674900" + }, + { + "asDouble": 0, + "attributes": [ + { + "key": "instance", + "value": { + "stringValue": "4" + } + } + ], + "timeUnixNano": "1646857199239674900" + }, + { + "asDouble": 0, + "attributes": [ + { + "key": "instance", + "value": { + "stringValue": "5" + } + } + ], + "timeUnixNano": "1646857199239674900" + }, + { + "asDouble": 0, + "attributes": [ + { + "key": "instance", + "value": { + "stringValue": "6" + } + } + ], + "timeUnixNano": "1646857199239674900" + }, + { + "asDouble": 0, + "attributes": [ + { + "key": "instance", + "value": { + "stringValue": "7" + } + } + ], + "timeUnixNano": "1646857199239674900" + } + ] + }, + "name": "cpu.idle", + "unit": "%" + }, + { + "description": "number of bytes committed to memory", + "gauge": { + "dataPoints": [ + { + "asDouble": 19516747776, + "timeUnixNano": "1646857199239674900" + } + ] + }, + "name": "bytes.committed", + "unit": "By" + }, + { + "description": "amount of time processor is busy", + "gauge": { + "dataPoints": [ + { + "asDouble": 0, + "attributes": [ + { + "key": "instance", + "value": { + "stringValue": "1" + } + } + ], + "timeUnixNano": "1646857199239674900" + }, + { + "asDouble": 0, + "attributes": [ + { + "key": "instance", + "value": { + "stringValue": "2" + } + } + ], + "timeUnixNano": "1646857199239674900" + } + ] + }, + "name": "processor.time", + "unit": "%" + } + ] + } + ], + "resource": {} + } + ] +} diff --git a/receiver/windowsperfcountersreceiver/testdata/scraper/sum_metric.json b/receiver/windowsperfcountersreceiver/testdata/scraper/sum_metric.json new file mode 100644 index 000000000000..49e50334da1e --- /dev/null +++ b/receiver/windowsperfcountersreceiver/testdata/scraper/sum_metric.json @@ -0,0 +1,28 @@ +{ + "resourceMetrics": [ + { + "instrumentationLibraryMetrics": [ + { + "instrumentationLibrary": {}, + "metrics": [ + { + "description": "number of bytes committed to memory", + "name": "bytes.committed", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "19446169600", + "timeUnixNano": "1646862225775600200" + } + ] + }, + "unit": "By" + } + ] + } + ], + "resource": {} + } + ] +} diff --git a/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper.go b/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper.go index ce450f28c488..8f44ce1693c8 100644 --- a/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper.go +++ b/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper.go @@ -44,18 +44,20 @@ type PerfCounterScraper interface { // scraper is the type that scrapes various host metrics. type scraper struct { - cfg *Config - logger *zap.Logger - counters []PerfCounterScraper + cfg *Config + settings component.TelemetrySettings + counters []PerfCounterMetrics + undefinedMetricCounters []PerfCounterScraper } -func newScraper(cfg *Config, logger *zap.Logger) (*scraper, error) { - if err := cfg.Validate(); err != nil { - return nil, err - } +type PerfCounterMetrics struct { + CounterScraper PerfCounterScraper + Attributes map[string]string + Metric string +} - s := &scraper{cfg: cfg, logger: logger} - return s, nil +func newScraper(cfg *Config, settings component.TelemetrySettings) *scraper { + return &scraper{cfg: cfg, settings: settings} } func (s *scraper) start(context.Context, component.Host) error { @@ -63,14 +65,18 @@ func (s *scraper) start(context.Context, component.Host) error { for _, perfCounterCfg := range s.cfg.PerfCounters { for _, instance := range perfCounterCfg.instances() { - for _, counterName := range perfCounterCfg.Counters { - counterPath := counterPath(perfCounterCfg.Object, instance, counterName) + for _, counterCfg := range perfCounterCfg.Counters { + counterPath := counterPath(perfCounterCfg.Object, instance, counterCfg.Name) c, err := pdh.NewPerfCounter(counterPath, true) if err != nil { errs = multierr.Append(errs, fmt.Errorf("counter %v: %w", counterPath, err)) } else { - s.counters = append(s.counters, c) + if counterCfg.Metric == "" { + s.undefinedMetricCounters = append(s.undefinedMetricCounters, c) + continue + } + s.counters = append(s.counters, PerfCounterMetrics{CounterScraper: c, Metric: counterCfg.Metric, Attributes: counterCfg.Attributes}) } } } @@ -78,7 +84,7 @@ func (s *scraper) start(context.Context, component.Host) error { // log a warning if some counters cannot be loaded, but do not crash the app if errs != nil { - s.logger.Warn("some performance counters could not be initialized", zap.Error(errs)) + s.settings.Logger.Warn("some performance counters could not be initialized", zap.Error(errs)) } return nil @@ -96,7 +102,7 @@ func (s *scraper) shutdown(context.Context) error { var errs error for _, counter := range s.counters { - errs = multierr.Append(errs, counter.Close()) + errs = multierr.Append(errs, counter.CounterScraper.Close()) } return errs @@ -106,35 +112,59 @@ func (s *scraper) scrape(context.Context) (pdata.Metrics, error) { md := pdata.NewMetrics() metrics := md.ResourceMetrics().AppendEmpty().InstrumentationLibraryMetrics().AppendEmpty().Metrics() now := pdata.NewTimestampFromTime(time.Now()) - var errs error metrics.EnsureCapacity(len(s.counters)) - for _, counter := range s.counters { + + for name, metricCfg := range s.cfg.MetricMetaData { + builtMetric := metrics.AppendEmpty() + + builtMetric.SetName(name) + builtMetric.SetDescription(metricCfg.Description) + builtMetric.SetUnit(metricCfg.Unit) + + if (metricCfg.Sum != SumMetric{}) { + builtMetric.SetDataType(pdata.MetricDataTypeSum) + builtMetric.Sum().SetIsMonotonic(metricCfg.Sum.Monotonic) + + switch metricCfg.Sum.Aggregation { + case "cumulative": + builtMetric.Sum().SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) + case "delta": + builtMetric.Sum().SetAggregationTemporality(pdata.MetricAggregationTemporalityDelta) + } + } else { + builtMetric.SetDataType(pdata.MetricDataTypeGauge) + } + + for _, counter := range s.counters { + if counter.Metric == builtMetric.Name() { + counterValues, err := counter.CounterScraper.ScrapeData() + if err != nil { + errs = multierr.Append(errs, err) + continue + } + initializeMetricDps(builtMetric, now, counterValues, counter.Attributes) + } + } + } + + for _, counter := range s.undefinedMetricCounters { counterValues, err := counter.ScrapeData() if err != nil { errs = multierr.Append(errs, err) continue } - initializeDoubleGaugeMetric(metrics.AppendEmpty(), now, counter.Path(), counterValues) + builtMetric := metrics.AppendEmpty() + builtMetric.SetName(counter.Path()) + builtMetric.SetDataType(pdata.MetricDataTypeGauge) + initializeMetricDps(builtMetric, now, counterValues, nil) } return md, errs } -func initializeDoubleGaugeMetric(metric pdata.Metric, now pdata.Timestamp, name string, counterValues []win_perf_counters.CounterValue) { - metric.SetName(name) - metric.SetDataType(pdata.MetricDataTypeGauge) - - dg := metric.Gauge() - ddps := dg.DataPoints() - ddps.EnsureCapacity(len(counterValues)) - for _, counterValue := range counterValues { - initializeNumberDataPointAsDouble(ddps.AppendEmpty(), now, counterValue.InstanceName, counterValue.Value) - } -} - func initializeNumberDataPointAsDouble(dataPoint pdata.NumberDataPoint, now pdata.Timestamp, instanceLabel string, value float64) { if instanceLabel != "" { dataPoint.Attributes().InsertString(instanceLabelName, instanceLabel) @@ -143,3 +173,30 @@ func initializeNumberDataPointAsDouble(dataPoint pdata.NumberDataPoint, now pdat dataPoint.SetTimestamp(now) dataPoint.SetDoubleVal(value) } + +func initializeMetricDps(metric pdata.Metric, now pdata.Timestamp, counterValues []win_perf_counters.CounterValue, attributes map[string]string) { + var dps pdata.NumberDataPointSlice + + if metric.DataType() == pdata.MetricDataTypeGauge { + dps = metric.Gauge().DataPoints() + } else { + dps = metric.Sum().DataPoints() + } + + dps.EnsureCapacity(len(counterValues)) + for _, counterValue := range counterValues { + dp := dps.AppendEmpty() + if attributes != nil { + for attKey, attVal := range attributes { + dp.Attributes().InsertString(attKey, attVal) + } + } + + if counterValue.InstanceName != "" { + dp.Attributes().InsertString(instanceLabelName, counterValue.InstanceName) + } + + dp.SetTimestamp(now) + dp.SetIntVal(int64(counterValue.Value)) + } +} diff --git a/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper_test.go b/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper_test.go index 504eaae3341f..892080781839 100644 --- a/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper_test.go +++ b/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper_test.go @@ -20,6 +20,7 @@ package windowsperfcountersreceiver import ( "context" "errors" + "path/filepath" "testing" "time" @@ -31,6 +32,8 @@ import ( "go.uber.org/zap/zapcore" "go.uber.org/zap/zaptest/observer" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/scrapertest" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/scrapertest/golden" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters" ) @@ -60,49 +63,77 @@ func (mpc *mockPerfCounter) Close() error { } func Test_WindowsPerfCounterScraper(t *testing.T) { - type expectedMetric struct { - name string - instanceLabelValues []string - } - type testCase struct { name string cfg *Config - newErr string mockCounterPath string startMessage string startErr string scrapeErr error shutdownErr error - expectedMetrics []expectedMetric + expectedMetricPath string } - defaultConfig := &Config{ - PerfCounters: []PerfCounterConfig{ - {Object: "Memory", Counters: []string{"Committed Bytes"}}, - }, - ScraperControllerSettings: scraperhelper.ScraperControllerSettings{CollectionInterval: time.Minute}, - } + defaultConfig := createDefaultConfig().(*Config) testCases := []testCase{ { name: "Standard", cfg: &Config{ + MetricMetaData: map[string]MetricConfig{ + "cpu.idle": { + Description: "percentage of time CPU is idle.", + Unit: "%", + Gauge: GaugeMetric{}, + }, + "bytes.committed": { + Description: "number of bytes committed to memory", + Unit: "By", + Gauge: GaugeMetric{}, + }, + "processor.time": { + Description: "amount of time processor is busy", + Unit: "%", + Gauge: GaugeMetric{}, + }, + }, PerfCounters: []PerfCounterConfig{ - {Object: "Memory", Counters: []string{"Committed Bytes"}}, - {Object: "Processor", Instances: []string{"*"}, Counters: []string{"% Processor Time"}}, - {Object: "Processor", Instances: []string{"1", "2"}, Counters: []string{"% Idle Time"}}, + {Object: "Memory", Counters: []CounterConfig{{Name: "Committed Bytes", Metric: "bytes.committed"}}}, + {Object: "Processor", Instances: []string{"*"}, Counters: []CounterConfig{{Name: "% Idle Time", Metric: "cpu.idle"}}}, + {Object: "Processor", Instances: []string{"1", "2"}, Counters: []CounterConfig{{Name: "% Processor Time", Metric: "processor.time"}}}, }, ScraperControllerSettings: scraperhelper.ScraperControllerSettings{CollectionInterval: time.Minute}, }, - expectedMetrics: []expectedMetric{ - {name: `\Memory\Committed Bytes`}, - {name: `\Processor(*)\% Processor Time`, instanceLabelValues: []string{"*"}}, - {name: `\Processor(1)\% Idle Time`, instanceLabelValues: []string{"1"}}, - {name: `\Processor(2)\% Idle Time`, instanceLabelValues: []string{"2"}}, + expectedMetricPath: filepath.Join("testdata", "scraper", "standard.json"), + }, + { + name: "SumMetric", + cfg: &Config{ + MetricMetaData: map[string]MetricConfig{ + "bytes.committed": { + Description: "number of bytes committed to memory", + Unit: "By", + Sum: SumMetric{}, + }, + }, + PerfCounters: []PerfCounterConfig{ + {Object: "Memory", Counters: []CounterConfig{{Name: "Committed Bytes", Metric: "bytes.committed"}}}, + }, + ScraperControllerSettings: scraperhelper.ScraperControllerSettings{CollectionInterval: time.Minute}, + }, + expectedMetricPath: filepath.Join("testdata", "scraper", "sum_metric.json"), + }, + { + name: "NoMetricDefinition", + cfg: &Config{ + PerfCounters: []PerfCounterConfig{ + {Object: "Memory", Counters: []CounterConfig{{Name: "Committed Bytes"}}}, + }, + ScraperControllerSettings: scraperhelper.ScraperControllerSettings{CollectionInterval: time.Minute}, }, + expectedMetricPath: filepath.Join("testdata", "scraper", "no_metric_def.json"), }, { name: "InvalidCounter", @@ -110,11 +141,11 @@ func Test_WindowsPerfCounterScraper(t *testing.T) { PerfCounters: []PerfCounterConfig{ { Object: "Memory", - Counters: []string{"Committed Bytes"}, + Counters: []CounterConfig{{Name: "Committed Bytes", Metric: "Committed Bytes"}}, }, { Object: "Invalid Object", - Counters: []string{"Invalid Counter"}, + Counters: []CounterConfig{{Name: "Invalid Counter", Metric: "invalid"}}, }, }, ScraperControllerSettings: scraperhelper.ScraperControllerSettings{CollectionInterval: time.Minute}, @@ -124,12 +155,11 @@ func Test_WindowsPerfCounterScraper(t *testing.T) { }, { name: "ScrapeError", - scrapeErr: errors.New("err1"), + scrapeErr: errors.New("err2"), }, { - name: "CloseError", - expectedMetrics: []expectedMetric{{name: ""}}, - shutdownErr: errors.New("err1"), + name: "CloseError", + shutdownErr: errors.New("err1"), }, } @@ -142,14 +172,11 @@ func Test_WindowsPerfCounterScraper(t *testing.T) { core, obs := observer.New(zapcore.WarnLevel) logger := zap.New(core) - scraper, err := newScraper(cfg, logger) - if test.newErr != "" { - require.EqualError(t, err, test.newErr) - return - } + settings := componenttest.NewNopTelemetrySettings() + settings.Logger = logger + scraper := newScraper(cfg, settings) - err = scraper.start(context.Background(), componenttest.NewNopHost()) - require.NoError(t, err) + err := scraper.start(context.Background(), componenttest.NewNopHost()) if test.startErr != "" { require.Equal(t, 1, obs.Len()) log := obs.All()[0] @@ -162,66 +189,37 @@ func Test_WindowsPerfCounterScraper(t *testing.T) { require.NoError(t, err) if test.mockCounterPath != "" || test.scrapeErr != nil || test.shutdownErr != nil { - for i := range scraper.counters { - scraper.counters[i] = newMockPerfCounter(test.mockCounterPath, test.scrapeErr, test.shutdownErr) + scraper.cfg.MetricMetaData = map[string]MetricConfig{ + "metric": { + Description: "desc", + Unit: "1", + Gauge: GaugeMetric{}, + }, + } + scraper.counters = []PerfCounterMetrics{ + { + CounterScraper: newMockPerfCounter(test.mockCounterPath, test.scrapeErr, test.shutdownErr), + Metric: "metric", + }, } } - md, err := scraper.scrape(context.Background()) + actualMetrics, err := scraper.scrape(context.Background()) if test.scrapeErr != nil { - assert.Equal(t, err, test.scrapeErr) - } else { - require.NoError(t, err) - } - metrics := md.ResourceMetrics().At(0).InstrumentationLibraryMetrics().At(0).Metrics() - require.Equal(t, len(test.expectedMetrics), metrics.Len()) - for i, e := range test.expectedMetrics { - metric := metrics.At(i) - assert.Equal(t, e.name, metric.Name()) - - ddp := metric.Gauge().DataPoints() - - var allInstances bool - for _, v := range e.instanceLabelValues { - if v == "*" { - allInstances = true - break - } - } - - if allInstances { - require.GreaterOrEqual(t, ddp.Len(), 1) - } else { - expectedDataPoints := 1 - if len(e.instanceLabelValues) > 0 { - expectedDataPoints = len(e.instanceLabelValues) - } - - require.Equal(t, expectedDataPoints, ddp.Len()) - } - - if len(e.instanceLabelValues) > 0 { - instanceLabelValues := make([]string, 0, ddp.Len()) - for i := 0; i < ddp.Len(); i++ { - instanceLabelValue, ok := ddp.At(i).Attributes().Get(instanceLabelName) - require.Truef(t, ok, "data point was missing %q label", instanceLabelName) - instanceLabelValues = append(instanceLabelValues, instanceLabelValue.StringVal()) - } - - if !allInstances { - for _, v := range e.instanceLabelValues { - assert.Contains(t, instanceLabelValues, v) - } - } - } + require.Equal(t, test.scrapeErr, err) + return } + require.NoError(t, err) err = scraper.shutdown(context.Background()) if test.shutdownErr != nil { - assert.Equal(t, err, test.shutdownErr) - } else { - require.NoError(t, err) + assert.Equal(t, test.shutdownErr, err) + return } + require.NoError(t, err) + expectedMetrics, err := golden.ReadMetrics(test.expectedMetricPath) + scrapertest.CompareMetrics(expectedMetrics, actualMetrics, scrapertest.IgnoreMetricValues) + require.NoError(t, err) }) } }