From 8eec12f2688309fe4bf0b97ad73128ab12d72861 Mon Sep 17 00:00:00 2001 From: Dmitrii Anoshin Date: Fri, 20 May 2022 11:06:59 -0700 Subject: [PATCH] [receiver/windowsperfcounter] Move configs from pkg/winperfcounters (#10022) This refactoring reduces coupling between `windowsperfcounterreceiver` and `pkg/winperfcounters` packages by moving config structs specific to `windowsperfcounterreceiver` along with other logic related to the configs from `pkg/winperfcounters`. --- pkg/winperfcounters/config.go | 33 ----- pkg/winperfcounters/config_windows.go | 32 ----- pkg/winperfcounters/go.mod | 2 - pkg/winperfcounters/go.sum | 7 - pkg/winperfcounters/watcher.go | 86 +----------- pkg/winperfcounters/watcher_test.go | 132 +++--------------- .../activedirectorydsreceiver/counters.go | 17 +-- .../activedirectorydsreceiver/scraper_test.go | 5 - receiver/iisreceiver/scraper_test.go | 5 - receiver/sqlserverreceiver/scraper.go | 2 +- .../windowsperfcountersreceiver/config.go | 24 +++- .../config_test.go | 35 +++-- .../factory_others_test.go | 6 +- .../factory_test.go | 18 ++- .../factory_windows_test.go | 6 +- .../windowsperfcounters_scraper.go | 105 +++++++++----- .../windowsperfcounters_scraper_test.go | 115 +++++++++++++-- 17 files changed, 252 insertions(+), 378 deletions(-) delete mode 100644 pkg/winperfcounters/config.go delete mode 100644 pkg/winperfcounters/config_windows.go diff --git a/pkg/winperfcounters/config.go b/pkg/winperfcounters/config.go deleted file mode 100644 index b5e43d68bd5e..000000000000 --- a/pkg/winperfcounters/config.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package winperfcounters // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/winperfcounters" - -// ObjectConfig defines configuration for a perf counter object. -type ObjectConfig struct { - Object string `mapstructure:"object"` - Instances []string `mapstructure:"instances"` - Counters []CounterConfig `mapstructure:"counters"` -} - -// CounterConfig defines the individual counter in an object. -type CounterConfig struct { - Name string `mapstructure:"name"` - MetricRep `mapstructure:",squash"` -} - -type MetricRep struct { - Name string `mapstructure:"metric"` - Attributes map[string]string `mapstructure:"attributes"` -} diff --git a/pkg/winperfcounters/config_windows.go b/pkg/winperfcounters/config_windows.go deleted file mode 100644 index c7115263a5ac..000000000000 --- a/pkg/winperfcounters/config_windows.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build windows -// +build windows - -package winperfcounters // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/winperfcounters" - -func (pc *ObjectConfig) instances() []string { - if len(pc.Instances) == 0 { - return []string{""} - } - - for _, instance := range pc.Instances { - if instance == "*" { - return []string{"*"} - } - } - - return pc.Instances -} diff --git a/pkg/winperfcounters/go.mod b/pkg/winperfcounters/go.mod index 2b0d2ba9e16c..c303ca2ffdec 100644 --- a/pkg/winperfcounters/go.mod +++ b/pkg/winperfcounters/go.mod @@ -4,7 +4,6 @@ go 1.17 require ( github.com/stretchr/testify v1.7.1 - go.uber.org/multierr v1.8.0 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 ) @@ -13,7 +12,6 @@ require ( github.com/kr/pretty v0.3.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.6.2 // indirect - go.uber.org/atomic v1.9.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/pkg/winperfcounters/go.sum b/pkg/winperfcounters/go.sum index 459651730cc0..ddf8ec0c18b7 100644 --- a/pkg/winperfcounters/go.sum +++ b/pkg/winperfcounters/go.sum @@ -16,15 +16,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/winperfcounters/watcher.go b/pkg/winperfcounters/watcher.go index 7a4202fe30dd..e7f50e0c86f3 100644 --- a/pkg/winperfcounters/watcher.go +++ b/pkg/winperfcounters/watcher.go @@ -20,11 +20,12 @@ package winperfcounters // import "github.com/open-telemetry/opentelemetry-colle import ( "fmt" - "go.uber.org/multierr" - "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/winperfcounters/internal/pdh" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/winperfcounters/internal/third_party/telegraf/win_perf_counters" ) +// TODO: This package became redundant as a wrapper. pkg/winperfcounters/internal/pdh can be moved here. + var _ PerfCounterWatcher = (*Watcher)(nil) // PerfCounterWatcher represents how to scrape data @@ -35,16 +36,11 @@ type PerfCounterWatcher interface { ScrapeData() ([]CounterValue, error) // Close all counters/handles related to the query and free all associated memory. Close() error - // GetMetricRep gets the representation of the metric the watcher is connected to - GetMetricRep() MetricRep } -const instanceLabelName = "instance" +type CounterValue = win_perf_counters.CounterValue -type Watcher struct { - Counter *pdh.PerfCounter - MetricRep -} +type Watcher = pdh.PerfCounter // NewWatcher creates new PerfCounterWatcher by provided parts of its path. func NewWatcher(object, instance, counterName string) (PerfCounterWatcher, error) { @@ -53,72 +49,7 @@ func NewWatcher(object, instance, counterName string) (PerfCounterWatcher, error if err != nil { return nil, fmt.Errorf("failed to create perf counter with path %v: %w", path, err) } - return Watcher{Counter: counter}, nil -} - -func (w Watcher) Path() string { - return w.Counter.Path() -} - -func (w Watcher) ScrapeData() ([]CounterValue, error) { - scrapedCounterValues, err := w.Counter.ScrapeData() - if err != nil { - return []CounterValue{}, err - } - - counterValues := []CounterValue{} - for _, counterValue := range scrapedCounterValues { - metric := w.GetMetricRep() - if counterValue.InstanceName != "" { - if metric.Attributes == nil { - metric.Attributes = map[string]string{instanceLabelName: counterValue.InstanceName} - } - metric.Attributes[instanceLabelName] = counterValue.InstanceName - } - counterValues = append(counterValues, CounterValue{MetricRep: metric, Value: counterValue.Value}) - } - return counterValues, nil -} - -func (w Watcher) Close() error { - return w.Counter.Close() -} - -func (w Watcher) GetMetricRep() MetricRep { - return w.MetricRep -} - -// BuildPaths creates watchers and their paths from configs. -func (objCfg ObjectConfig) BuildPaths() ([]PerfCounterWatcher, error) { - var errs error - var watchers []PerfCounterWatcher - - for _, instance := range objCfg.instances() { - for _, counterCfg := range objCfg.Counters { - counterPath := counterPath(objCfg.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 { - newWatcher := Watcher{Counter: c} - - if counterCfg.MetricRep.Name != "" { - metricCfg := MetricRep{Name: counterCfg.MetricRep.Name} - if counterCfg.Attributes != nil { - metricCfg.Attributes = counterCfg.Attributes - } - newWatcher.MetricRep = metricCfg - } else { - newWatcher.MetricRep.Name = c.Path() - } - - watchers = append(watchers, newWatcher) - } - } - } - - return watchers, errs + return counter, nil } func counterPath(object, instance, counterName string) string { @@ -128,8 +59,3 @@ func counterPath(object, instance, counterName string) string { return fmt.Sprintf("\\%s%s\\%s", object, instance, counterName) } - -type CounterValue struct { - MetricRep - Value float64 -} diff --git a/pkg/winperfcounters/watcher_test.go b/pkg/winperfcounters/watcher_test.go index 90ff00b3238a..0f91b6f5122e 100644 --- a/pkg/winperfcounters/watcher_test.go +++ b/pkg/winperfcounters/watcher_test.go @@ -21,136 +21,46 @@ import ( "testing" "github.com/stretchr/testify/require" - "go.uber.org/multierr" ) -// Test_PathBuilder tests that paths are built correctly given a ObjectConfig -func Test_PathBuilder(t *testing.T) { +func TestCounterPath(t *testing.T) { testCases := []struct { - name string - cfgs []ObjectConfig - expectedErr string - expectedPaths []string + name string + object string + instance string + counterName string + expectedPath string }{ { - name: "basicPath", - cfgs: []ObjectConfig{ - { - Object: "Memory", - Counters: []CounterConfig{{Name: "Committed Bytes"}}, - }, - }, - expectedPaths: []string{"\\Memory\\Committed Bytes"}, + name: "basicPath", + object: "Memory", + counterName: "Committed Bytes", + expectedPath: "\\Memory\\Committed Bytes", }, { - name: "multiplePaths", - cfgs: []ObjectConfig{ - { - Object: "Memory", - Counters: []CounterConfig{{Name: "Committed Bytes"}}, - }, - { - Object: "Memory", - Counters: []CounterConfig{{Name: "Available Bytes"}}, - }, - }, - expectedPaths: []string{"\\Memory\\Committed Bytes", "\\Memory\\Available Bytes"}, - }, - { - name: "multipleIndividualCounters", - cfgs: []ObjectConfig{ - { - Object: "Memory", - Counters: []CounterConfig{ - {Name: "Committed Bytes"}, - {Name: "Available Bytes"}, - }, - }, - { - Object: "Memory", - Counters: []CounterConfig{}, - }, - }, - expectedPaths: []string{"\\Memory\\Committed Bytes", "\\Memory\\Available Bytes"}, - }, - { - name: "invalidCounter", - cfgs: []ObjectConfig{ - { - Object: "Broken", - Counters: []CounterConfig{{Name: "Broken Counter"}}, - }, - }, - - expectedErr: "counter \\Broken\\Broken Counter: The specified object was not found on the computer.\r\n", - }, - { - name: "multipleInvalidCounters", - cfgs: []ObjectConfig{ - { - Object: "Broken", - Counters: []CounterConfig{{Name: "Broken Counter"}}, - }, - { - Object: "Broken part 2", - Counters: []CounterConfig{{Name: "Broken again"}}, - }, - }, - expectedErr: "counter \\Broken\\Broken Counter: The specified object was not found on the computer.\r\n; counter \\Broken part 2\\Broken again: The specified object was not found on the computer.\r\n", + name: "basicPathWithInstance", + object: "Web Service", + instance: "_Total", + counterName: "Current Connections", + expectedPath: "\\Web Service(_Total)\\Current Connections", }, } for _, test := range testCases { t.Run(test.name, func(t *testing.T) { - var errs error - allWatchers := []PerfCounterWatcher{} - for _, cfg := range test.cfgs { - watchers, err := cfg.BuildPaths() - if err != nil { - errs = multierr.Append(errs, err) - continue - } - allWatchers = append(allWatchers, watchers...) - } - - if test.expectedErr != "" { - require.EqualError(t, errs, test.expectedErr) - return - } - - actualPaths := []string{} - for _, watcher := range allWatchers { - actualPaths = append(actualPaths, watcher.Path()) - } - - require.Equal(t, test.expectedPaths, actualPaths) + path := counterPath(test.object, test.instance, test.counterName) + require.Equal(t, test.expectedPath, path) }) } } // Test_Scraping_Wildcard tests that wildcard instances pull out values func Test_Scraping_Wildcard(t *testing.T) { - counterVals := []CounterValue{} - var errs error - - cfg := ObjectConfig{ - Object: "LogicalDisk", - Instances: []string{"*"}, - Counters: []CounterConfig{{Name: "Free Megabytes"}}, - } - watchers, err := cfg.BuildPaths() + watcher, err := NewWatcher("LogicalDisk", "*", "Free Megabytes") require.NoError(t, err) - for _, watcher := range watchers { - value, err := watcher.ScrapeData() - if err != nil { - errs = multierr.Append(errs, err) - continue - } - - require.NoError(t, err) - counterVals = append(counterVals, value...) - } + values, err := watcher.ScrapeData() + require.NoError(t, err) - require.GreaterOrEqual(t, len(counterVals), 3) + require.GreaterOrEqual(t, len(values), 3) } diff --git a/receiver/activedirectorydsreceiver/counters.go b/receiver/activedirectorydsreceiver/counters.go index a4b28a41266a..37a88b1745c5 100644 --- a/receiver/activedirectorydsreceiver/counters.go +++ b/receiver/activedirectorydsreceiver/counters.go @@ -243,20 +243,5 @@ const ( type defaultWatcherCreater struct{} func (defaultWatcherCreater) Create(counterName string) (winperfcounters.PerfCounterWatcher, error) { - conf := winperfcounters.ObjectConfig{ - Object: object, - Instances: []string{instanceName}, - Counters: []winperfcounters.CounterConfig{ - { - Name: counterName, - }, - }, - } - - watchers, err := conf.BuildPaths() - if err != nil { - return nil, err - } - - return watchers[0], nil + return winperfcounters.NewWatcher(object, instanceName, counterName) } diff --git a/receiver/activedirectorydsreceiver/scraper_test.go b/receiver/activedirectorydsreceiver/scraper_test.go index e87df412585c..8d6419de1b1a 100644 --- a/receiver/activedirectorydsreceiver/scraper_test.go +++ b/receiver/activedirectorydsreceiver/scraper_test.go @@ -177,8 +177,3 @@ func (w mockPerfCounterWatcher) Close() error { return w.closeErr } - -// GetMetricRep panics; It should not be called -func (mockPerfCounterWatcher) GetMetricRep() winperfcounters.MetricRep { - panic("mockPerfCounterWatcher::GetMetricRep is not implemented") -} diff --git a/receiver/iisreceiver/scraper_test.go b/receiver/iisreceiver/scraper_test.go index 1d1d1368ee59..c7b98fe1ef7d 100644 --- a/receiver/iisreceiver/scraper_test.go +++ b/receiver/iisreceiver/scraper_test.go @@ -99,7 +99,6 @@ func TestScrapeFailure(t *testing.T) { type mockPerfCounter struct { watchErr error value float64 - winperfcounters.MetricRep } func newMockWatcherFactory(watchErr error, value float64) func(string, string, @@ -123,7 +122,3 @@ func (mpc *mockPerfCounter) ScrapeData() ([]winperfcounters.CounterValue, error) func (mpc *mockPerfCounter) Close() error { return nil } - -func (mpc *mockPerfCounter) GetMetricRep() winperfcounters.MetricRep { - return winperfcounters.MetricRep{} -} diff --git a/receiver/sqlserverreceiver/scraper.go b/receiver/sqlserverreceiver/scraper.go index 28fab075061f..b9012b1baa30 100644 --- a/receiver/sqlserverreceiver/scraper.go +++ b/receiver/sqlserverreceiver/scraper.go @@ -97,7 +97,7 @@ func recordersPerDatabase(watcherRecorders []watcherRecorder) (map[string][]curr } for _, counterValue := range counterValues { - dbName := counterValue.Attributes["instance"] + dbName := counterValue.InstanceName // it's important to initialize new values for the closure. val := counterValue.Value diff --git a/receiver/windowsperfcountersreceiver/config.go b/receiver/windowsperfcountersreceiver/config.go index 2c184335a3a8..35bc6c522e0f 100644 --- a/receiver/windowsperfcountersreceiver/config.go +++ b/receiver/windowsperfcountersreceiver/config.go @@ -19,16 +19,14 @@ import ( "go.opentelemetry.io/collector/receiver/scraperhelper" "go.uber.org/multierr" - - winperfcounters "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/winperfcounters" ) // Config defines configuration for WindowsPerfCounters receiver. type Config struct { scraperhelper.ScraperControllerSettings `mapstructure:",squash"` - MetricMetaData map[string]MetricConfig `mapstructure:"metrics"` - PerfCounters []winperfcounters.ObjectConfig `mapstructure:"perfcounters"` + MetricMetaData map[string]MetricConfig `mapstructure:"metrics"` + PerfCounters []ObjectConfig `mapstructure:"perfcounters"` } // MetricsConfig defines the configuration for a metric to be created. @@ -47,6 +45,24 @@ type SumMetric struct { Monotonic bool `mapstructure:"monotonic"` } +// ObjectConfig defines configuration for a perf counter object. +type ObjectConfig struct { + Object string `mapstructure:"object"` + Instances []string `mapstructure:"instances"` + Counters []CounterConfig `mapstructure:"counters"` +} + +// CounterConfig defines the individual counter in an object. +type CounterConfig struct { + Name string `mapstructure:"name"` + MetricRep `mapstructure:",squash"` +} + +type MetricRep struct { + Name string `mapstructure:"metric"` + Attributes map[string]string `mapstructure:"attributes"` +} + func (c *Config) Validate() error { var errs error diff --git a/receiver/windowsperfcountersreceiver/config_test.go b/receiver/windowsperfcountersreceiver/config_test.go index b21308e39543..6a74b87ea6ca 100644 --- a/receiver/windowsperfcountersreceiver/config_test.go +++ b/receiver/windowsperfcountersreceiver/config_test.go @@ -26,8 +26,6 @@ import ( "go.opentelemetry.io/collector/config" "go.opentelemetry.io/collector/receiver/scraperhelper" "go.opentelemetry.io/collector/service/servicetest" - - winperfcounters "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/winperfcounters" ) func TestLoadConfig(t *testing.T) { @@ -46,13 +44,13 @@ func TestLoadConfig(t *testing.T) { r0 := cfg.Receivers[config.NewComponentID(typeStr)] defaultConfigSingleObject := factory.CreateDefaultConfig() - counterConfig := winperfcounters.CounterConfig{ + counterConfig := CounterConfig{ Name: "counter1", - MetricRep: winperfcounters.MetricRep{ + MetricRep: MetricRep{ Name: "metric", }, } - defaultConfigSingleObject.(*Config).PerfCounters = []winperfcounters.ObjectConfig{{Object: "object", Counters: []winperfcounters.CounterConfig{counterConfig}}} + defaultConfigSingleObject.(*Config).PerfCounters = []ObjectConfig{{Object: "object", Counters: []CounterConfig{counterConfig}}} defaultConfigSingleObject.(*Config).MetricMetaData = map[string]MetricConfig{ "metric": { Description: "desc", @@ -63,10 +61,9 @@ func TestLoadConfig(t *testing.T) { assert.Equal(t, defaultConfigSingleObject, r0) - counterConfig2 := winperfcounters.CounterConfig{ + counterConfig2 := CounterConfig{ Name: "counter2", - MetricRep: winperfcounters.MetricRep{ - + MetricRep: MetricRep{ Name: "metric2", }, } @@ -77,14 +74,14 @@ func TestLoadConfig(t *testing.T) { ReceiverSettings: config.NewReceiverSettings(config.NewComponentIDWithName(typeStr, "customname")), CollectionInterval: 30 * time.Second, }, - PerfCounters: []winperfcounters.ObjectConfig{ + PerfCounters: []ObjectConfig{ { Object: "object1", - Counters: []winperfcounters.CounterConfig{counterConfig}, + Counters: []CounterConfig{counterConfig}, }, { Object: "object2", - Counters: []winperfcounters.CounterConfig{counterConfig, counterConfig2}, + Counters: []CounterConfig{counterConfig, counterConfig2}, }, }, MetricMetaData: map[string]MetricConfig{ @@ -114,10 +111,10 @@ func TestLoadConfigMetrics(t *testing.T) { TestName: "NoMetricsDefined", TestPath: filepath.Join("testdata", "config-nometrics.yaml"), Expected: Config{ - PerfCounters: []winperfcounters.ObjectConfig{ + PerfCounters: []ObjectConfig{ { Object: "object", - Counters: []winperfcounters.CounterConfig{{Name: "counter1"}}, + Counters: []CounterConfig{{Name: "counter1"}}, }, }, }, @@ -126,10 +123,10 @@ func TestLoadConfigMetrics(t *testing.T) { TestName: "NoMetricSpecified", TestPath: filepath.Join("testdata", "config-nometricspecified.yaml"), Expected: Config{ - PerfCounters: []winperfcounters.ObjectConfig{ + PerfCounters: []ObjectConfig{ { Object: "object", - Counters: []winperfcounters.CounterConfig{{Name: "counter1"}}, + Counters: []CounterConfig{{Name: "counter1"}}, }, }, MetricMetaData: map[string]MetricConfig{ @@ -145,10 +142,10 @@ func TestLoadConfigMetrics(t *testing.T) { TestName: "SumMetric", TestPath: filepath.Join("testdata", "config-summetric.yaml"), Expected: Config{ - PerfCounters: []winperfcounters.ObjectConfig{ + PerfCounters: []ObjectConfig{ { Object: "object", - Counters: []winperfcounters.CounterConfig{{Name: "counter1", MetricRep: winperfcounters.MetricRep{Name: "metric"}}}, + Counters: []CounterConfig{{Name: "counter1", MetricRep: MetricRep{Name: "metric"}}}, }, }, MetricMetaData: map[string]MetricConfig{ @@ -167,10 +164,10 @@ func TestLoadConfigMetrics(t *testing.T) { TestName: "MetricUnspecifiedType", TestPath: filepath.Join("testdata", "config-unspecifiedmetrictype.yaml"), Expected: Config{ - PerfCounters: []winperfcounters.ObjectConfig{ + PerfCounters: []ObjectConfig{ { Object: "object", - Counters: []winperfcounters.CounterConfig{{Name: "counter1", MetricRep: winperfcounters.MetricRep{Name: "metric"}}}, + Counters: []CounterConfig{{Name: "counter1", MetricRep: MetricRep{Name: "metric"}}}, }, }, MetricMetaData: map[string]MetricConfig{ diff --git a/receiver/windowsperfcountersreceiver/factory_others_test.go b/receiver/windowsperfcountersreceiver/factory_others_test.go index eec840f8c08b..52e38ca70826 100644 --- a/receiver/windowsperfcountersreceiver/factory_others_test.go +++ b/receiver/windowsperfcountersreceiver/factory_others_test.go @@ -23,17 +23,15 @@ import ( "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/consumer/consumertest" - - "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/winperfcounters" ) func TestCreateMetricsReceiver(t *testing.T) { factory := NewFactory() cfg := factory.CreateDefaultConfig() - cfg.(*Config).PerfCounters = []winperfcounters.ObjectConfig{ + cfg.(*Config).PerfCounters = []ObjectConfig{ { Object: "object", - Counters: []winperfcounters.CounterConfig{{Name: "counter", MetricRep: winperfcounters.MetricRep{Name: "metric"}}}, + Counters: []CounterConfig{{Name: "counter", MetricRep: MetricRep{Name: "metric"}}}, }, } diff --git a/receiver/windowsperfcountersreceiver/factory_test.go b/receiver/windowsperfcountersreceiver/factory_test.go index ef44ee8c9543..8598d99ad74b 100644 --- a/receiver/windowsperfcountersreceiver/factory_test.go +++ b/receiver/windowsperfcountersreceiver/factory_test.go @@ -23,8 +23,6 @@ import ( "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/config/configtest" "go.opentelemetry.io/collector/consumer/consumertest" - - winperfcounters "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/winperfcounters" ) var creationParams = componenttest.NewNopReceiverCreateSettings() @@ -36,10 +34,10 @@ func TestCreateDefaultConfig(t *testing.T) { assert.NotNil(t, cfg, "failed to create default config") assert.NoError(t, configtest.CheckConfigStruct(cfg)) - cfg.(*Config).PerfCounters = []winperfcounters.ObjectConfig{ + cfg.(*Config).PerfCounters = []ObjectConfig{ { Object: "object", - Counters: []winperfcounters.CounterConfig{{Name: "counter", MetricRep: winperfcounters.MetricRep{Name: "metric"}}}, + Counters: []CounterConfig{{Name: "counter", MetricRep: MetricRep{Name: "metric"}}}, }, } @@ -57,10 +55,10 @@ func TestCreateDefaultConfig(t *testing.T) { func TestCreateTracesReceiver(t *testing.T) { factory := NewFactory() cfg := factory.CreateDefaultConfig() - cfg.(*Config).PerfCounters = []winperfcounters.ObjectConfig{ + cfg.(*Config).PerfCounters = []ObjectConfig{ { Object: "object", - Counters: []winperfcounters.CounterConfig{{Name: "counter", MetricRep: winperfcounters.MetricRep{Name: "metric"}}}, + Counters: []CounterConfig{{Name: "counter", MetricRep: MetricRep{Name: "metric"}}}, }, } @@ -80,10 +78,10 @@ func TestCreateTracesReceiver(t *testing.T) { func TestCreateTracesReceiverNoMetrics(t *testing.T) { factory := NewFactory() cfg := factory.CreateDefaultConfig() - cfg.(*Config).PerfCounters = []winperfcounters.ObjectConfig{ + cfg.(*Config).PerfCounters = []ObjectConfig{ { Object: "object", - Counters: []winperfcounters.CounterConfig{{Name: "counter"}}, + Counters: []CounterConfig{{Name: "counter"}}, }, } tReceiver, err := factory.CreateTracesReceiver(context.Background(), creationParams, cfg, consumertest.NewNop()) @@ -95,10 +93,10 @@ func TestCreateTracesReceiverNoMetrics(t *testing.T) { func TestCreateLogsReceiver(t *testing.T) { factory := NewFactory() cfg := factory.CreateDefaultConfig() - cfg.(*Config).PerfCounters = []winperfcounters.ObjectConfig{ + cfg.(*Config).PerfCounters = []ObjectConfig{ { Object: "object", - Counters: []winperfcounters.CounterConfig{{Name: "counter", MetricRep: winperfcounters.MetricRep{Name: "metric"}}}, + Counters: []CounterConfig{{Name: "counter", MetricRep: MetricRep{Name: "metric"}}}, }, } diff --git a/receiver/windowsperfcountersreceiver/factory_windows_test.go b/receiver/windowsperfcountersreceiver/factory_windows_test.go index d0f6577fb91d..eb3bbb8ddb99 100644 --- a/receiver/windowsperfcountersreceiver/factory_windows_test.go +++ b/receiver/windowsperfcountersreceiver/factory_windows_test.go @@ -23,17 +23,15 @@ import ( "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/consumer/consumertest" - - winperfcounters "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/winperfcounters" ) func TestCreateMetricsReceiver(t *testing.T) { factory := NewFactory() cfg := factory.CreateDefaultConfig() - cfg.(*Config).PerfCounters = []winperfcounters.ObjectConfig{ + cfg.(*Config).PerfCounters = []ObjectConfig{ { Object: "object", - Counters: []winperfcounters.CounterConfig{{Name: "counter", MetricRep: winperfcounters.MetricRep{Name: "metric"}}}, + Counters: []CounterConfig{{Name: "counter", MetricRep: MetricRep{Name: "metric"}}}, }, } diff --git a/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper.go b/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper.go index 7f34e2cadd65..ec55b4dae18c 100644 --- a/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper.go +++ b/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper.go @@ -32,34 +32,63 @@ import ( const instanceLabelName = "instance" +type perfCounterMetricWatcher struct { + winperfcounters.PerfCounterWatcher + MetricRep +} + // scraper is the type that scrapes various host metrics. type scraper struct { cfg *Config settings component.TelemetrySettings - watchers []winperfcounters.PerfCounterWatcher + watchers []perfCounterMetricWatcher } func newScraper(cfg *Config, settings component.TelemetrySettings) *scraper { - return &scraper{cfg: cfg, settings: settings} + return &scraper{cfg: cfg, settings: settings, watchers: []perfCounterMetricWatcher{}} } func (s *scraper) start(context.Context, component.Host) error { - watchers := []winperfcounters.PerfCounterWatcher{} - for _, objCfg := range s.cfg.PerfCounters { - objWatchers, err := objCfg.BuildPaths() - if err != nil { - s.settings.Logger.Warn("some performance counters could not be initialized", zap.Error(err)) - continue - } - for _, objWatcher := range objWatchers { - watchers = append(watchers, objWatcher) - } + watchers, err := initWatchers(s.cfg.PerfCounters) + if err != nil { + s.settings.Logger.Warn("some performance counters could not be initialized", zap.Error(err)) } s.watchers = watchers - return nil } +func initWatchers(objConfigs []ObjectConfig) ([]perfCounterMetricWatcher, error) { + var errs error + var watchers []perfCounterMetricWatcher + + for _, objCfg := range objConfigs { + for _, instance := range instancesFromConfig(objCfg) { + for _, counterCfg := range objCfg.Counters { + pcw, err := winperfcounters.NewWatcher(objCfg.Object, instance, counterCfg.Name) + if err != nil { + errs = multierr.Append(errs, err) + continue + } + + watcher := perfCounterMetricWatcher{ + PerfCounterWatcher: pcw, + MetricRep: MetricRep{Name: pcw.Path()}, + } + if counterCfg.MetricRep.Name != "" { + watcher.MetricRep.Name = counterCfg.MetricRep.Name + if counterCfg.MetricRep.Attributes != nil { + watcher.MetricRep.Attributes = counterCfg.MetricRep.Attributes + } + } + + watchers = append(watchers, watcher) + } + } + } + + return watchers, errs +} + func (s *scraper) shutdown(context.Context) error { var errs error for _, watcher := range s.watchers { @@ -103,35 +132,32 @@ func (s *scraper) scrape(context.Context) (pmetric.Metrics, error) { metrics[name] = builtMetric } - counterVals := []winperfcounters.CounterValue{} for _, watcher := range s.watchers { - scrapedCounterValues, err := watcher.ScrapeData() + counterVals, err := watcher.ScrapeData() if err != nil { errs = multierr.Append(errs, err) continue } - counterVals = append(counterVals, scrapedCounterValues...) - } - for _, scrapedValue := range counterVals { - var metric pmetric.Metric - metricRep := scrapedValue.MetricRep - if builtmetric, ok := metrics[metricRep.Name]; ok { - metric = builtmetric - } else { - metric = metricSlice.AppendEmpty() - metric.SetDataType(pmetric.MetricDataTypeGauge) - metric.SetName(metricRep.Name) - metric.SetUnit("1") - } + for _, val := range counterVals { + var metric pmetric.Metric + if builtmetric, ok := metrics[watcher.MetricRep.Name]; ok { + metric = builtmetric + } else { + metric = metricSlice.AppendEmpty() + metric.SetDataType(pmetric.MetricDataTypeGauge) + metric.SetName(watcher.MetricRep.Name) + metric.SetUnit("1") + } - initializeMetricDps(metric, now, scrapedValue.Value, metricRep.Attributes) + initializeMetricDps(metric, now, val, watcher.MetricRep.Attributes) + } } - return md, errs } -func initializeMetricDps(metric pmetric.Metric, now pcommon.Timestamp, counterValue float64, attributes map[string]string) { +func initializeMetricDps(metric pmetric.Metric, now pcommon.Timestamp, counterValue winperfcounters.CounterValue, + attributes map[string]string) { var dps pmetric.NumberDataPointSlice if metric.DataType() == pmetric.MetricDataTypeGauge { @@ -141,6 +167,7 @@ func initializeMetricDps(metric pmetric.Metric, now pcommon.Timestamp, counterVa } dp := dps.AppendEmpty() + dp.Attributes().InsertString(instanceLabelName, counterValue.InstanceName) if attributes != nil { for attKey, attVal := range attributes { dp.Attributes().InsertString(attKey, attVal) @@ -149,5 +176,19 @@ func initializeMetricDps(metric pmetric.Metric, now pcommon.Timestamp, counterVa } dp.SetTimestamp(now) - dp.SetDoubleVal(counterValue) + dp.SetDoubleVal(counterValue.Value) +} + +func instancesFromConfig(oc ObjectConfig) []string { + if len(oc.Instances) == 0 { + return []string{""} + } + + for _, instance := range oc.Instances { + if instance == "*" { + return []string{"*"} + } + } + + return oc.Instances } diff --git a/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper_test.go b/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper_test.go index e7895a2028fa..e69e68ce3b56 100644 --- a/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper_test.go +++ b/receiver/windowsperfcountersreceiver/windowsperfcounters_scraper_test.go @@ -33,7 +33,6 @@ import ( "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/pkg/winperfcounters" ) type mockPerfCounter struct { @@ -77,10 +76,10 @@ func Test_WindowsPerfCounterScraper(t *testing.T) { Gauge: GaugeMetric{}, }, }, - PerfCounters: []winperfcounters.ObjectConfig{ - {Object: "Memory", Counters: []winperfcounters.CounterConfig{{Name: "Committed Bytes", MetricRep: winperfcounters.MetricRep{Name: "bytes.committed"}}}}, - {Object: "Processor", Instances: []string{"*"}, Counters: []winperfcounters.CounterConfig{{Name: "% Idle Time", MetricRep: winperfcounters.MetricRep{Name: "cpu.idle"}}}}, - {Object: "Processor", Instances: []string{"1", "2"}, Counters: []winperfcounters.CounterConfig{{Name: "% Processor Time", MetricRep: winperfcounters.MetricRep{Name: "processor.time"}}}}, + PerfCounters: []ObjectConfig{ + {Object: "Memory", Counters: []CounterConfig{{Name: "Committed Bytes", MetricRep: MetricRep{Name: "bytes.committed"}}}}, + {Object: "Processor", Instances: []string{"*"}, Counters: []CounterConfig{{Name: "% Idle Time", MetricRep: MetricRep{Name: "cpu.idle"}}}}, + {Object: "Processor", Instances: []string{"1", "2"}, Counters: []CounterConfig{{Name: "% Processor Time", MetricRep: MetricRep{Name: "processor.time"}}}}, }, ScraperControllerSettings: scraperhelper.ScraperControllerSettings{CollectionInterval: time.Minute}, }, @@ -96,8 +95,8 @@ func Test_WindowsPerfCounterScraper(t *testing.T) { Sum: SumMetric{}, }, }, - PerfCounters: []winperfcounters.ObjectConfig{ - {Object: "Memory", Counters: []winperfcounters.CounterConfig{{Name: "Committed Bytes", MetricRep: winperfcounters.MetricRep{Name: "bytes.committed"}}}}, + PerfCounters: []ObjectConfig{ + {Object: "Memory", Counters: []CounterConfig{{Name: "Committed Bytes", MetricRep: MetricRep{Name: "bytes.committed"}}}}, }, ScraperControllerSettings: scraperhelper.ScraperControllerSettings{CollectionInterval: time.Minute}, }, @@ -106,8 +105,8 @@ func Test_WindowsPerfCounterScraper(t *testing.T) { { name: "NoMetricDefinition", cfg: &Config{ - PerfCounters: []winperfcounters.ObjectConfig{ - {Object: "Memory", Counters: []winperfcounters.CounterConfig{{Name: "Committed Bytes"}}}, + PerfCounters: []ObjectConfig{ + {Object: "Memory", Counters: []CounterConfig{{Name: "Committed Bytes"}}}, }, ScraperControllerSettings: scraperhelper.ScraperControllerSettings{CollectionInterval: time.Minute}, }, @@ -116,20 +115,20 @@ func Test_WindowsPerfCounterScraper(t *testing.T) { { name: "InvalidCounter", cfg: &Config{ - PerfCounters: []winperfcounters.ObjectConfig{ + PerfCounters: []ObjectConfig{ { Object: "Memory", - Counters: []winperfcounters.CounterConfig{{Name: "Committed Bytes", MetricRep: winperfcounters.MetricRep{Name: "Committed Bytes"}}}, + Counters: []CounterConfig{{Name: "Committed Bytes", MetricRep: MetricRep{Name: "Committed Bytes"}}}, }, { Object: "Invalid Object", - Counters: []winperfcounters.CounterConfig{{Name: "Invalid Counter", MetricRep: winperfcounters.MetricRep{Name: "invalid"}}}, + Counters: []CounterConfig{{Name: "Invalid Counter", MetricRep: MetricRep{Name: "invalid"}}}, }, }, ScraperControllerSettings: scraperhelper.ScraperControllerSettings{CollectionInterval: time.Minute}, }, startMessage: "some performance counters could not be initialized", - startErr: "counter \\Invalid Object\\Invalid Counter: The specified object was not found on the computer.\r\n", + startErr: "failed to create perf counter with path \\Invalid Object\\Invalid Counter: The specified object was not found on the computer.\r\n", }, } @@ -170,3 +169,93 @@ func Test_WindowsPerfCounterScraper(t *testing.T) { }) } } + +func TestInitWatchers(t *testing.T) { + testCases := []struct { + name string + cfgs []ObjectConfig + expectedErr string + expectedPaths []string + }{ + { + name: "basicPath", + cfgs: []ObjectConfig{ + { + Object: "Memory", + Counters: []CounterConfig{{Name: "Committed Bytes"}}, + }, + }, + expectedPaths: []string{"\\Memory\\Committed Bytes"}, + }, + { + name: "multiplePaths", + cfgs: []ObjectConfig{ + { + Object: "Memory", + Counters: []CounterConfig{{Name: "Committed Bytes"}}, + }, + { + Object: "Memory", + Counters: []CounterConfig{{Name: "Available Bytes"}}, + }, + }, + expectedPaths: []string{"\\Memory\\Committed Bytes", "\\Memory\\Available Bytes"}, + }, + { + name: "multipleIndividualCounters", + cfgs: []ObjectConfig{ + { + Object: "Memory", + Counters: []CounterConfig{ + {Name: "Committed Bytes"}, + {Name: "Available Bytes"}, + }, + }, + { + Object: "Memory", + Counters: []CounterConfig{}, + }, + }, + expectedPaths: []string{"\\Memory\\Committed Bytes", "\\Memory\\Available Bytes"}, + }, + { + name: "invalidCounter", + cfgs: []ObjectConfig{ + { + Object: "Broken", + Counters: []CounterConfig{{Name: "Broken Counter"}}, + }, + }, + + expectedErr: "failed to create perf counter with path \\Broken\\Broken Counter: The specified object was not found on the computer.\r\n", + }, + { + name: "multipleInvalidCounters", + cfgs: []ObjectConfig{ + { + Object: "Broken", + Counters: []CounterConfig{{Name: "Broken Counter"}}, + }, + { + Object: "Broken part 2", + Counters: []CounterConfig{{Name: "Broken again"}}, + }, + }, + expectedErr: "failed to create perf counter with path \\Broken\\Broken Counter: The specified object was not found on the computer.\r\n; failed to create perf counter with path \\Broken part 2\\Broken again: The specified object was not found on the computer.\r\n", + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + watchers, errs := initWatchers(test.cfgs) + if test.expectedErr != "" { + require.EqualError(t, errs, test.expectedErr) + } else { + require.NoError(t, errs) + } + for i, watcher := range watchers { + require.Equal(t, test.expectedPaths[i], watcher.Path()) + } + }) + } +}