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

Changed collection method of the second counter value in the Windows Perfmon module of Metricbeat #32305

Merged
merged 16 commits into from
Sep 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ https://github.com/elastic/beats/compare/v8.2.0\...main[Check the HEAD diff]

*Metricbeat*

- in module/windows/perfmon, changed collection method of the second counter value required to create a displayable value {pull}32305[32305]
- Fix and improve AWS metric period calculation to avoid zero-length intervals {pull}32724[32724]

*Packetbeat*
Expand Down
63 changes: 18 additions & 45 deletions metricbeat/module/windows/perfmon/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,11 @@ import (
"fmt"
"regexp"
"strings"
"time"
"unicode"

"github.com/elastic/beats/v7/metricbeat/helper/windows/pdh"

"github.com/pkg/errors"

"math/rand"

"golang.org/x/sys/windows"

"github.com/elastic/beats/v7/metricbeat/mb"
"github.com/elastic/elastic-agent-libs/logp"
)
Expand All @@ -52,7 +47,6 @@ type Reader struct {
log *logp.Logger //
config Config // Metricset configuration
counters []PerfCounter
event windows.Handle
}

type PerfCounter struct {
Expand All @@ -72,18 +66,13 @@ func NewReader(config Config) (*Reader, error) {
if err := query.Open(); err != nil {
return nil, err
}
event, err := windows.CreateEvent(nil, 0, 0, nil)
if err != nil {
return nil, err
}
r := &Reader{
query: query,
log: logp.NewLogger("perfmon"),
config: config,
event: event,
}
r.mapCounters(config)
_, err = r.getCounterPaths()
_, err := r.getCounterPaths()
if err != nil {
return nil, err
}
Expand All @@ -94,11 +83,11 @@ func NewReader(config Config) (*Reader, error) {
func (re *Reader) RefreshCounterPaths() error {
newCounters, err := re.getCounterPaths()
if err != nil {
return errors.Wrap(err, "failed retrieving counter paths")
return fmt.Errorf("failed retrieving counter paths: %w", err)
}
err = re.query.RemoveUnusedCounters(newCounters)
if err != nil {
return errors.Wrap(err, "failed removing unused counter values")
return fmt.Errorf("failed removing unused counter values: %w", err)
}
return nil
}
Expand All @@ -110,17 +99,17 @@ func (re *Reader) Read() ([]mb.Event, error) {
if err := re.query.CollectData(); err != nil {
// users can encounter the case no counters are found (services/processes stopped), this should not generate an event with the error message,
//could be the case the specific services are started after and picked up by the next RefreshCounterPaths func
if err == pdh.PDH_NO_COUNTERS {
if err == pdh.PDH_NO_COUNTERS { //nolint:errorlint // Bad linter! This is always errno or nil.
re.log.Warnf("%s %v", collectFailedMsg, err)
} else {
return nil, errors.Wrap(err, collectFailedMsg)
return nil, fmt.Errorf("%v: %w", collectFailedMsg, err)
}
}

// Get the values.
values, err := re.getValues()
if err != nil {
return nil, errors.Wrap(err, "failed formatting counter values")
return nil, fmt.Errorf("failed formatting counter values: %w", err)
}
var events []mb.Event
// GroupAllCountersTo config option where counters for all instances are aggregated and instance count is added in the event under the string value provided by this option.
Expand All @@ -135,41 +124,25 @@ func (re *Reader) Read() ([]mb.Event, error) {

func (re *Reader) getValues() (map[string][]pdh.CounterValue, error) {
var val map[string][]pdh.CounterValue
var sec uint32 = 1
err := re.query.CollectDataEx(sec, re.event)
// Sleep for one second before collecting the second raw value-
time.Sleep(time.Second)

// Collect the second raw value.
err := re.query.CollectData()
if err != nil {
return nil, err
}
waitFor, err := windows.WaitForSingleObject(re.event, windows.INFINITE)

// Collect the displayable value.
val, err = re.query.GetFormattedCounterValues()
if err != nil {
return nil, err
}
switch waitFor {
case windows.WAIT_OBJECT_0:
val, err = re.query.GetFormattedCounterValues()
if err != nil {
return nil, err
}
case windows.WAIT_FAILED:
return nil, errors.New("WaitForSingleObject has failed")
default:
return nil, errors.New("WaitForSingleObject was abandoned or still waiting for completion")
}
return val, err
}

func randSeq(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}

// Close will close the PDH query for now.
func (re *Reader) Close() error {
defer windows.CloseHandle(re.event)
return re.query.Close()
}

Expand All @@ -181,23 +154,23 @@ func (re *Reader) getCounterPaths() ([]string, error) {
childQueries, err := re.query.GetCounterPaths(counter.QueryName)
if err != nil {
if re.config.IgnoreNECounters {
switch err {
switch err { //nolint:errorlint // Bad linter! This is always errno or nil.
case pdh.PDH_CSTATUS_NO_COUNTER, pdh.PDH_CSTATUS_NO_COUNTERNAME,
pdh.PDH_CSTATUS_NO_INSTANCE, pdh.PDH_CSTATUS_NO_OBJECT:
re.log.Infow("Ignoring non existent counter", "error", err,
logp.Namespace("perfmon"), "query", counter.QueryName)
continue
}
} else {
return newCounters, errors.Wrapf(err, `failed to expand counter (query="%v")`, counter.QueryName)
return newCounters, fmt.Errorf("failed to expand counter (query='%v'): %w", counter.QueryName, err)
}
}
newCounters = append(newCounters, childQueries...)
// there are cases when the ExpandWildCardPath will retrieve a successful status but not an expanded query so we need to check for the size of the list
if err == nil && len(childQueries) >= 1 && !strings.Contains(childQueries[0], "*") {
for _, v := range childQueries {
if err := re.query.AddCounter(v, counter.InstanceName, counter.Format, isWildcard(childQueries, counter.InstanceName)); err != nil {
return newCounters, errors.Wrapf(err, "failed to add counter (query='%v')", counter.QueryName)
return newCounters, fmt.Errorf("failed to add counter (query='%v'): %w", counter.QueryName, err)
}
re.counters[i].ChildQueries = append(re.counters[i].ChildQueries, v)
}
Expand Down