Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perfmon ignore non existent counters #6432

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions metricbeat/module/windows/perfmon/_meta/docs.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ performance counters.

You must configure queries for the Windows performance counters that you wish
to collect. The example below collects processor time and disk writes.
With `format` you can set the output format for a specific counter. Possible values are
`float` and `long`. If nothing is selected the default value is `float`.
`ignore_non_existent_counters` ignores failures for non-existent counters without
to interrupt the service. With `format` you can set the output format for a specific counter.
Possible values are `float` and `long`. If nothing is selected the default value is `float`.
With `instance_name`, you can specify the name of the instance. Use this setting when:
- You want to use an instance name that is different from the computed name. For example, `Total` instead of `_Total`.
- You specify a counter that has no instance. For example, `\TCPIP Performance Diagnostics\IPv4 NBLs/sec indicated without prevalidation`.
Expand All @@ -19,6 +20,7 @@ For wildcard queries this setting has no effect.
- module: windows
metricsets: ["perfmon"]
period: 10s
perfmon.ignore_non_existent_counters: true
perfmon.counters:
- instance_label: "processor.name"
instance_name: "Total"
Expand Down
75 changes: 55 additions & 20 deletions metricbeat/module/windows/perfmon/pdh_integration_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,13 @@ func TestQuery(t *testing.T) {
}

func TestExistingCounter(t *testing.T) {
config := make([]CounterConfig, 1)
config[0].InstanceLabel = "processor.name"
config[0].MeasurementLabel = "processor.time.total.pct"
config[0].Query = processorTimeCounter
config[0].Format = "float"
config := PerfmonConfig{
CounterConfig: make([]CounterConfig, 1),
}
config.CounterConfig[0].InstanceLabel = "processor.name"
config.CounterConfig[0].MeasurementLabel = "processor.time.total.pct"
config.CounterConfig[0].Query = processorTimeCounter
config.CounterConfig[0].Format = "float"
handle, err := NewPerfmonReader(config)
if err != nil {
t.Fatal(err)
Expand All @@ -105,11 +107,13 @@ func TestExistingCounter(t *testing.T) {
}

func TestNonExistingCounter(t *testing.T) {
config := make([]CounterConfig, 1)
config[0].InstanceLabel = "processor.name"
config[0].MeasurementLabel = "processor.time.total.pct"
config[0].Query = "\\Processor Information(_Total)\\not existing counter"
config[0].Format = "float"
config := PerfmonConfig{
CounterConfig: make([]CounterConfig, 1),
}
config.CounterConfig[0].InstanceLabel = "processor.name"
config.CounterConfig[0].MeasurementLabel = "processor.time.total.pct"
config.CounterConfig[0].Query = "\\Processor Information(_Total)\\not existing counter"
config.CounterConfig[0].Format = "float"
handle, err := NewPerfmonReader(config)
if assert.Error(t, err) {
assert.EqualValues(t, PDH_CSTATUS_NO_COUNTER, errors.Cause(err))
Expand All @@ -121,12 +125,41 @@ func TestNonExistingCounter(t *testing.T) {
}
}

func TestIgnoreNonExistentCounter(t *testing.T) {
config := PerfmonConfig{
CounterConfig: make([]CounterConfig, 1),
IgnoreNECounters: true,
}
config.CounterConfig[0].InstanceLabel = "processor.name"
config.CounterConfig[0].MeasurementLabel = "processor.time.total.pct"
config.CounterConfig[0].Query = "\\Processor Information(_Total)\\not existing counter"
config.CounterConfig[0].Format = "float"
handle, err := NewPerfmonReader(config)

values, err := handle.Read()

time.Sleep(time.Millisecond * 1000)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any other way instead of using Sleep here? It makes the test suite much slower and has always potential to break.


if assert.Error(t, err) {
assert.EqualValues(t, PDH_NO_DATA, errors.Cause(err))
}

if handle != nil {
err = handle.query.Close()
assert.NoError(t, err)
}

t.Log(values)
}

func TestNonExistingObject(t *testing.T) {
config := make([]CounterConfig, 1)
config[0].InstanceLabel = "processor.name"
config[0].MeasurementLabel = "processor.time.total.pct"
config[0].Query = "\\non existing object\\% Processor Performance"
config[0].Format = "float"
config := PerfmonConfig{
CounterConfig: make([]CounterConfig, 1),
}
config.CounterConfig[0].InstanceLabel = "processor.name"
config.CounterConfig[0].MeasurementLabel = "processor.time.total.pct"
config.CounterConfig[0].Query = "\\non existing object\\% Processor Performance"
config.CounterConfig[0].Format = "float"
handle, err := NewPerfmonReader(config)
if assert.Error(t, err) {
assert.EqualValues(t, PDH_CSTATUS_NO_OBJECT, errors.Cause(err))
Expand Down Expand Up @@ -253,11 +286,13 @@ func TestRawValues(t *testing.T) {
}

func TestWildcardQuery(t *testing.T) {
config := make([]CounterConfig, 1)
config[0].InstanceLabel = "processor.name"
config[0].MeasurementLabel = "processor.time.pct"
config[0].Query = `\Processor Information(*)\% Processor Time`
config[0].Format = "float"
config := PerfmonConfig{
CounterConfig: make([]CounterConfig, 1),
}
config.CounterConfig[0].InstanceLabel = "processor.name"
config.CounterConfig[0].MeasurementLabel = "processor.time.pct"
config.CounterConfig[0].Query = `\Processor Information(*)\% Processor Time`
config.CounterConfig[0].Format = "float"
handle, err := NewPerfmonReader(config)
if err != nil {
t.Fatal(err)
Expand Down
16 changes: 12 additions & 4 deletions metricbeat/module/windows/perfmon/pdh_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"unicode/utf16"
"unsafe"

"github.com/elastic/beats/libbeat/logp"

"github.com/joeshaw/multierror"
"github.com/pkg/errors"
"golang.org/x/sys/windows"
Expand Down Expand Up @@ -222,7 +224,7 @@ func (q *Query) AddCounter(counterPath string, format Format, instanceName strin

h, err := PdhAddCounter(q.handle, counterPath, 0)
if err != nil {
return errors.Wrapf(err, `failed to add counter (path="%v")`, counterPath)
return err
}

wildcard := wildcardRegexp.MatchString(counterPath)
Expand Down Expand Up @@ -314,7 +316,7 @@ type PerfmonReader struct {
executed bool // Indicates if the query has been executed.
}

func NewPerfmonReader(config []CounterConfig) (*PerfmonReader, error) {
func NewPerfmonReader(config PerfmonConfig) (*PerfmonReader, error) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported function NewPerfmonReader should have comment or be unexported

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported function NewPerfmonReader should have comment or be unexported

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported function NewPerfmonReader should have comment or be unexported

query, err := NewQuery("")
if err != nil {
return nil, err
Expand All @@ -326,7 +328,7 @@ func NewPerfmonReader(config []CounterConfig) (*PerfmonReader, error) {
measurement: map[string]string{},
}

for _, counter := range config {
for _, counter := range config.CounterConfig {
var format Format
switch counter.Format {
case "float":
Expand All @@ -335,8 +337,14 @@ func NewPerfmonReader(config []CounterConfig) (*PerfmonReader, error) {
format = LongFormat
}
if err := query.AddCounter(counter.Query, format, counter.InstanceName); err != nil {
if config.IgnoreNECounters {
if err == PDH_CSTATUS_NO_COUNTER {
logp.Info(`ignore non existent counter (path="%v")`, counter.Query)
continue
}
}
query.Close()
return nil, err
return nil, errors.Wrapf(err, `failed to add counter (path="%v")`, counter.Query)
}

r.instanceLabel[counter.Query] = counter.InstanceLabel
Expand Down
11 changes: 7 additions & 4 deletions metricbeat/module/windows/perfmon/perfmon.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ type CounterConfig struct {
Format string `config:"format"`
}

type PerfmonConfig struct {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported type PerfmonConfig should have comment or be unexported
type name will be used as perfmon.PerfmonConfig by other packages, and that stutters; consider calling this Config

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported type PerfmonConfig should have comment or be unexported
type name will be used as perfmon.PerfmonConfig by other packages, and that stutters; consider calling this Config

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported type PerfmonConfig should have comment or be unexported
type name will be used as perfmon.PerfmonConfig by other packages, and that stutters; consider calling this Config

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type name will be used as perfmon.PerfmonConfig by other packages, and that stutters; consider calling this Config

IgnoreNECounters bool `config:"perfmon.ignore_non_existent_counters"`
CounterConfig []CounterConfig `config:"perfmon.counters" validate:"required"`
}

func init() {
if err := mb.Registry.AddMetricSet("windows", "perfmon", New); err != nil {
panic(err)
Expand All @@ -36,9 +41,7 @@ type MetricSet struct {
func New(base mb.BaseMetricSet) (mb.MetricSet, error) {
cfgwarn.Beta("The perfmon metricset is beta")

config := struct {
CounterConfig []CounterConfig `config:"perfmon.counters" validate:"required"`
}{}
config := PerfmonConfig{}

if err := base.Module().UnpackConfig(&config); err != nil {
return nil, err
Expand All @@ -57,7 +60,7 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) {

}

reader, err := NewPerfmonReader(config.CounterConfig)
reader, err := NewPerfmonReader(config)
if err != nil {
return nil, errors.Wrap(err, "initialization failed")
}
Expand Down