diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 1bf2cc8f762..320cc2a72de 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -376,6 +376,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Fix retrieving resources by ID for the azure module. {pull}21711[21711] {issue}21707[21707] - Use timestamp from CloudWatch API when creating events. {pull}21498[21498] - Report the correct windows events for system/filesystem {pull}21758[21758] +- Fix regular expression in windows/permfon. {pull}22146[22146] {issue}21125[21125] - Fix azure storage event format. {pull}21845[21845] - Fix panic in kubernetes autodiscover related to keystores {issue}21843[21843] {pull}21880[21880] - [Kubernetes] Remove redundant dockersock volume mount {pull}22009[22009] diff --git a/metricbeat/helper/windows/pdh/pdh_query_windows.go b/metricbeat/helper/windows/pdh/pdh_query_windows.go index 3c51df5073a..8cc3a46edc8 100644 --- a/metricbeat/helper/windows/pdh/pdh_query_windows.go +++ b/metricbeat/helper/windows/pdh/pdh_query_windows.go @@ -30,7 +30,7 @@ import ( ) var ( - instanceNameRegexp = regexp.MustCompile(`.*?\((.*?)\).*`) + instanceNameRegexp = regexp.MustCompile(`(\(.+\))\\`) objectNameRegexp = regexp.MustCompile(`(?:^\\\\[^\\]+\\|^\\)([^\\]+)`) ) @@ -86,7 +86,7 @@ func (q *Query) AddCounter(counterPath string, instance string, format string, w var instanceName string // Extract the instance name from the counterPath. if instance == "" || wildcard { - instanceName, err = MatchInstanceName(counterPath) + instanceName, err = matchInstanceName(counterPath) if err != nil { return err } @@ -233,18 +233,50 @@ func (q *Query) Close() error { return PdhCloseQuery(q.Handle) } -// MatchInstanceName will check first for instance and then for any objects names. -func MatchInstanceName(counterPath string) (string, error) { +// matchInstanceName will check first for instance and then for any objects names. +func matchInstanceName(counterPath string) (string, error) { matches := instanceNameRegexp.FindStringSubmatch(counterPath) - if len(matches) != 2 { - matches = objectNameRegexp.FindStringSubmatch(counterPath) + if len(matches) == 2 { + return returnLastInstance(matches[1]), nil } + matches = objectNameRegexp.FindStringSubmatch(counterPath) if len(matches) == 2 { return matches[1], nil } return "", errors.New("query doesn't contain an instance name. In this case you have to define 'instance_name'") } +// returnLastInstance will return the content from the last parentheses, this covers cases as `\WF (System.Workflow) 4.0.0.0(*)\Workflows Created`. +func returnLastInstance(match string) string { + var openedParanth int + var innerMatch string + var matches []string + runeMatch := []rune(match) + for i := 0; i < len(runeMatch); i++ { + char := string(runeMatch[i]) + + // check if string ends between parentheses + if char == ")" { + openedParanth -= 1 + } + if openedParanth > 0 { + innerMatch += char + } + if openedParanth == 0 && innerMatch != "" { + matches = append(matches, innerMatch) + innerMatch = "" + } + // check if string starts between parentheses + if char == "(" { + openedParanth += 1 + } + } + if len(matches) > 0 { + return matches[len(matches)-1] + } + return match +} + // getCounterValue will retrieve the counter value based on the format applied in the config options func getCounterValue(counter *Counter) CounterValue { counterValue := CounterValue{Instance: counter.instanceName, Err: CounterValueError{CStatus: 0}} diff --git a/metricbeat/helper/windows/pdh/pdh_query_windows_test.go b/metricbeat/helper/windows/pdh/pdh_query_windows_test.go index 20d894d3924..3a09d6d77b5 100644 --- a/metricbeat/helper/windows/pdh/pdh_query_windows_test.go +++ b/metricbeat/helper/windows/pdh/pdh_query_windows_test.go @@ -89,6 +89,48 @@ func TestSuccessfulQuery(t *testing.T) { assert.NotNil(t, list) } +func TestMatchInstanceName(t *testing.T) { + query := "\\SQLServer:Databases(*)\\Log File(s) Used Size (KB)" + match, err := matchInstanceName(query) + assert.NoError(t, err) + assert.Equal(t, match, "*") + + query = " \\\\desktop-rfooe09\\per processor network interface card activity(3, microsoft wi-fi directvirtual (gyfyg) adapter #2)\\dpcs queued/sec" + match, err = matchInstanceName(query) + assert.NoError(t, err) + assert.Equal(t, match, "3, microsoft wi-fi directvirtual (gyfyg) adapter #2") + + query = " \\\\desktop-rfooe09\\ (test this scenario) per processor network interface card activity(3, microsoft wi-fi directvirtual (gyfyg) adapter #2)\\dpcs queued/sec" + match, err = matchInstanceName(query) + assert.NoError(t, err) + assert.Equal(t, match, "3, microsoft wi-fi directvirtual (gyfyg) adapter #2") + + query = "\\RAS\\Bytes Received By Disconnected Clients" + match, err = matchInstanceName(query) + assert.NoError(t, err) + assert.Equal(t, match, "RAS") + + query = `\\Process (chrome.exe#4)\\Bytes Received By Disconnected Clients` + match, err = matchInstanceName(query) + assert.NoError(t, err) + assert.Equal(t, match, "chrome.exe#4") + + query = "\\BranchCache\\Local Cache: Cache complete file segments" + match, err = matchInstanceName(query) + assert.NoError(t, err) + assert.Equal(t, match, "BranchCache") + + query = `\Synchronization(*)\Exec. Resource no-Waits AcqShrdStarveExcl/sec` + match, err = matchInstanceName(query) + assert.NoError(t, err) + assert.Equal(t, match, "*") + + query = `\.NET CLR Exceptions(test hellp (dsdsd) #rfsfs #3)\# of Finallys / sec` + match, err = matchInstanceName(query) + assert.NoError(t, err) + assert.Equal(t, match, "test hellp (dsdsd) #rfsfs #3") +} + // TestInstanceNameRegexp tests regular expression for instance. func TestInstanceNameRegexp(t *testing.T) { queryPaths := []string{`\SQLServer:Databases(*)\Log File(s) Used Size (KB)`, `\Search Indexer(*)\L0 Indexes (Wordlists)`, @@ -96,7 +138,7 @@ func TestInstanceNameRegexp(t *testing.T) { for _, path := range queryPaths { matches := instanceNameRegexp.FindStringSubmatch(path) if assert.Len(t, matches, 2, "regular expression did not return any matches") { - assert.Equal(t, matches[1], "*") + assert.Equal(t, matches[1], "(*)") } } } @@ -114,6 +156,28 @@ func TestObjectNameRegexp(t *testing.T) { } } +func TestReturnLastInstance(t *testing.T) { + query := "(*)" + match := returnLastInstance(query) + assert.Equal(t, match, "*") + + query = "(3, microsoft wi-fi directvirtual (gyfyg) adapter #2)" + match = returnLastInstance(query) + assert.Equal(t, match, "3, microsoft wi-fi directvirtual (gyfyg) adapter #2") + + query = "(test this scenario) per processor network interface card activity(3, microsoft wi-fi directvirtual (gyfyg) adapter #2)" + match = returnLastInstance(query) + assert.Equal(t, match, "3, microsoft wi-fi directvirtual (gyfyg) adapter #2") + + query = `(chrome.exe#4)` + match = returnLastInstance(query) + assert.Equal(t, match, "chrome.exe#4") + + query = `(test hellp (dsdsd) #rfsfs #3)` + match = returnLastInstance(query) + assert.Equal(t, match, "test hellp (dsdsd) #rfsfs #3") +} + func TestUTF16ToStringArray(t *testing.T) { var array = []string{"\\\\DESKTOP-RFOOE09\\Physikalischer Datenträger(0 C:)\\Schreibvorgänge/s", "\\\\DESKTOP-RFOOE09\\Physikalischer Datenträger(_Total)\\Schreibvorgänge/s", ""} var unicode []uint16 diff --git a/metricbeat/module/windows/perfmon/data.go b/metricbeat/module/windows/perfmon/data.go index 833c5757220..6e4e9c6ef0a 100644 --- a/metricbeat/module/windows/perfmon/data.go +++ b/metricbeat/module/windows/perfmon/data.go @@ -33,7 +33,7 @@ import ( "github.com/elastic/beats/v7/metricbeat/mb" ) -var processRegexp = regexp.MustCompile(`(.+?)#[1-9]+`) +var processRegexp = regexp.MustCompile(`(.+?[^\s])(?:#\d+|$)`) func (re *Reader) groupToEvents(counters map[string][]pdh.CounterValue) []mb.Event { eventMap := make(map[string]*mb.Event) @@ -73,7 +73,7 @@ func (re *Reader) groupToEvents(counters map[string][]pdh.CounterValue) []mb.Eve Error: errors.Wrapf(val.Err.Error, "failed on query=%v", counterPath), } if val.Instance != "" { - //will ignore instance counter + //will ignore instance index if ok, match := matchesParentProcess(val.Instance); ok { eventMap[eventKey].MetricSetFields.Put(counter.InstanceField, match) } else { diff --git a/metricbeat/module/windows/perfmon/data_test.go b/metricbeat/module/windows/perfmon/data_test.go index df23d1667ff..7203963d2cc 100644 --- a/metricbeat/module/windows/perfmon/data_test.go +++ b/metricbeat/module/windows/perfmon/data_test.go @@ -159,9 +159,13 @@ func TestGroupToSingleEvent(t *testing.T) { func TestMatchesParentProcess(t *testing.T) { ok, val := matchesParentProcess("svchost") - assert.False(t, ok) + assert.True(t, ok) assert.Equal(t, val, "svchost") ok, val = matchesParentProcess("svchost#54") assert.True(t, ok) assert.Equal(t, val, "svchost") + + ok, val = matchesParentProcess("svchost (test) another #54") + assert.True(t, ok) + assert.Equal(t, val, "svchost (test) another #54") }