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

Add metricbeat iis module #15059

Merged
merged 45 commits into from
Feb 28, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
9500d18
created iis module
narph Nov 19, 2018
bab0cd4
work in progress
narph Mar 27, 2019
66dfa82
Merge branch 'master' into iis-module
narph Dec 11, 2019
b1fba8f
iis changes
narph Dec 11, 2019
7a8ff78
add iis module
narph Dec 11, 2019
6bd81eb
light
narph Jan 15, 2020
3b7b520
Merge branch 'master' into iis-module
narph Jan 15, 2020
f5cb0a7
work on build
narph Jan 15, 2020
c320a53
work on build
narph Jan 15, 2020
9d5df82
build
narph Jan 15, 2020
222648e
fmt update
narph Jan 15, 2020
00e1a08
temp
narph Jan 16, 2020
258ded3
work on website
narph Jan 17, 2020
5f30e5a
temp
narph Jan 21, 2020
7ab4d2d
Merge branch 'master' into iis-module
narph Jan 21, 2020
92e5873
temp
narph Jan 22, 2020
f18b298
manifest changes
narph Jan 23, 2020
b770c57
temp
narph Jan 24, 2020
ea28cbe
temp
narph Jan 28, 2020
397296b
work on wp
narph Jan 31, 2020
411d9d9
adding application pool metricset
narph Feb 5, 2020
113eec5
wmi option
narph Feb 10, 2020
6aa632b
test
narph Feb 10, 2020
ca3f589
work on apppool
narph Feb 11, 2020
b739816
work on app pool
narph Feb 12, 2020
552c1b7
work on website metricset
narph Feb 12, 2020
d245579
Merge branch 'master' into iis-module
narph Feb 12, 2020
fadb63a
work on tests
narph Feb 13, 2020
706b393
Work on website
narph Feb 14, 2020
a1656be
work on website
narph Feb 14, 2020
4ab7fa8
Merge branch 'master' into iis-module
narph Feb 14, 2020
1d760b1
work on website
narph Feb 14, 2020
9645ff0
perfmon fix
narph Feb 14, 2020
ef247b8
work on websie
narph Feb 17, 2020
2e44357
update config
narph Feb 18, 2020
2e1cc66
Merge branch 'master' into iis-module
narph Feb 19, 2020
ecaa80f
feedback
narph Feb 19, 2020
fcfd4f3
work on feedback
narph Feb 21, 2020
118f55c
temp
narph Feb 24, 2020
d7b9eb9
Merge branch 'master' into iis-module
narph Feb 24, 2020
f8ab974
ecs
narph Feb 24, 2020
a40569e
Merge branch 'master' into iis-module
narph Feb 25, 2020
fbb7cd5
temp
narph Feb 25, 2020
5f1b016
add counters
narph Feb 26, 2020
ac81ebe
Merge branch 'master' into iis-module
narph Feb 27, 2020
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
4 changes: 3 additions & 1 deletion metricbeat/docs/modules/iis.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ The `iis` module mericsets are `webserver`, `website` and `application_pool`.

[float]
=== `webserver`
This metricset allows users to retrieve aggregated metrics for the entire webserver.
A light metricset using the windows perfmon metricset as the base metricset.
This metricset allows users to retrieve aggregated metrics for the entire webserver,

[float]
=== `website`
A light metricset using the windows perfmon metricset as the base metricset.
This metricset will collect metrics of specific sites, users can configure which websites they want to monitor, else, all are considered.

[float]
Expand Down
7 changes: 4 additions & 3 deletions metricbeat/helper/windows/pdh/pdh_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ type PdhCounterHandle uintptr

var InvalidCounterHandle = ^PdhCounterHandle(0)

const PERF_DETAIL_WIZARD = 400
// counter detail level
narph marked this conversation as resolved.
Show resolved Hide resolved
const PerformanceDetailWizard = 400

// PdhCounterInfo struct contains the performance counter details
type PdhCounterInfo struct {
Expand Down Expand Up @@ -267,7 +268,7 @@ func PdhEnumObjectItems(objectName string) ([]uint16, []uint16, error) {
&cBuffSize,
&iBuff[0],
&iBuffSize,
PERF_DETAIL_WIZARD,
PerformanceDetailWizard,
0); err != nil {
if PdhErrno(err.(syscall.Errno)) != PDH_MORE_DATA {
return nil, nil, PdhErrno(err.(syscall.Errno))
Expand All @@ -283,7 +284,7 @@ func PdhEnumObjectItems(objectName string) ([]uint16, []uint16, error) {
&cBuffSize,
&iBuff[0],
&iBuffSize,
PERF_DETAIL_WIZARD,
PerformanceDetailWizard,
0); err != nil {
return nil, nil, err
}
Expand Down
4 changes: 3 additions & 1 deletion metricbeat/module/iis/_meta/docs.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ The `iis` module mericsets are `webserver`, `website` and `application_pool`.

[float]
=== `webserver`
This metricset allows users to retrieve aggregated metrics for the entire webserver.
A light metricset using the windows perfmon metricset as the base metricset.
This metricset allows users to retrieve aggregated metrics for the entire webserver,

[float]
=== `website`
A light metricset using the windows perfmon metricset as the base metricset.
This metricset will collect metrics of specific sites, users can configure which websites they want to monitor, else, all are considered.

[float]
Expand Down
9 changes: 9 additions & 0 deletions metricbeat/module/iis/application_pool/application_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,12 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error {
}
return nil
}

// Close will be called when metricbeat is stopped, should close the query.
func (m *MetricSet) Close() error {
err := m.reader.close()
if err != nil {
return errors.Wrap(err, "failed to close pdh query")
}
return nil
}
4 changes: 2 additions & 2 deletions metricbeat/module/iis/application_pool/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func getApplicationPools(names []string) ([]ApplicationPool, error) {
return filtered, nil
}

// getw3wpProceses func retrieves the running w3wp process ids
// getw3wpProceses func retrieves the running w3wp process ids. A worker process is a windows process (w3wp.exe) which runs Web applications, and is responsible for handling requests sent to a Web Server for a specific application pool.
Copy link
Member

Choose a reason for hiding this comment

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

Nit. Very long line.

func getw3wpProceses() (map[int]string, error) {
processes, err := sysinfo.Processes()
if err != nil {
Expand All @@ -232,7 +232,7 @@ func getw3wpProceses() (map[int]string, error) {
for i, ar := range info.Args {
if ar == "-ap" && len(info.Args) > i+1 {
wps[info.PID] = info.Args[i+1]
continue
break
}
}
}
Expand Down
1 change: 1 addition & 0 deletions metricbeat/module/windows/perfmon/perfmon.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ func (m *MetricSet) Close() error {
return nil
}

// validateCounterConfig func will check if the instanceLabel has been set, the instance label is mandatory inside the pefrmon metricset
func validateCounterConfig(counters []CounterConfig) error {
for _, counter := range counters {
if counter.InstanceLabel == "" {
Expand Down
105 changes: 38 additions & 67 deletions metricbeat/module/windows/perfmon/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,16 @@ func NewReader(config Config) (*Reader, error) {
}

// RefreshCounterPaths will recheck for any new instances and add them to the counter list
func (this *Reader) RefreshCounterPaths() error {
func (re *Reader) RefreshCounterPaths() error {
var newCounters []string
for _, counter := range this.config.CounterConfig {
childQueries, err := this.query.GetCounterPaths(counter.Query)
for _, counter := range re.config.CounterConfig {
childQueries, err := re.query.GetCounterPaths(counter.Query)
if err != nil {
if this.config.IgnoreNECounters {
if re.config.IgnoreNECounters {
switch err {
case pdh.PDH_CSTATUS_NO_COUNTER, pdh.PDH_CSTATUS_NO_COUNTERNAME,
pdh.PDH_CSTATUS_NO_INSTANCE, pdh.PDH_CSTATUS_NO_OBJECT:
this.log.Infow("Ignoring non existent counter", "error", err,
re.log.Infow("Ignoring non existent counter", "error", err,
logp.Namespace("perfmon"), "query", counter.Query)
continue
}
Expand All @@ -120,15 +120,15 @@ func (this *Reader) RefreshCounterPaths() error {
// 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 := this.query.AddCounter(v, counter.InstanceName, counter.Format, len(childQueries) > 1); err != nil {
if err := re.query.AddCounter(v, counter.InstanceName, counter.Format, len(childQueries) > 1); err != nil {
return errors.Wrapf(err, "failed to add counter (query='%v')", counter.Query)
}
this.instanceLabel[v] = counter.InstanceLabel
this.measurement[v] = counter.MeasurementLabel
re.instanceLabel[v] = counter.InstanceLabel
re.measurement[v] = counter.MeasurementLabel
}
}
}
err := this.query.RemoveUnusedCounters(newCounters)
err := re.query.RemoveUnusedCounters(newCounters)
if err != nil {
return errors.Wrap(err, "failed removing unused counter values")
}
Expand All @@ -137,50 +137,45 @@ func (this *Reader) RefreshCounterPaths() error {
}

// Read executes a query and returns those values in an event.
func (this *Reader) Read(metricset string) ([]mb.Event, error) {
func (re *Reader) Read(metricset string) ([]mb.Event, error) {
// Some counters, such as rate counters, require two counter values in order to compute a displayable value. In this case we must call PdhCollectQueryData twice before calling PdhGetFormattedCounterValue.
// For more information, see Collecting Performance Data (https://docs.microsoft.com/en-us/windows/desktop/PerfCtrs/collecting-performance-data).
if err := this.query.CollectData(); err != nil {
if err := re.query.CollectData(); err != nil {
return nil, errors.Wrap(err, "failed querying counter values")
}

// Get the values.
values, err := this.query.GetFormattedCounterValues()
values, err := re.query.GetFormattedCounterValues()
if err != nil {
return nil, errors.Wrap(err, "failed formatting counter values")
}
var events []mb.Event
if metricsetName != metricset {
// will be enabled when https://github.com/elastic/beats/issues/16366
//if len(this.config.FilterByInstance) > 0 {
// values = filterValuesByInstances(this.config.FilterByInstance, values)
//}
if this.config.GroupAllCountersTo != "" {
event := this.groupToEvent(values)
events = append(events, event)
}
// GroupAllCountersTo config option will only apply to the webserver metricset, where counters for all instances are aggregated
if metricsetName != metricset && re.config.GroupAllCountersTo != "" {
Copy link
Member

Choose a reason for hiding this comment

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

Similarly here, I would prefer to don't depend on the metricset name for certain features, I would prefer this to be more explicit.
Also this generic metricset shouldn't have something specific for the webserver metricset (or any other metricset) if possible.

event := re.groupToEvent(values)
events = append(events, event)
} else {
events = this.groupToEvents(values)
events = re.groupToEvents(values)
}
this.executed = true
re.executed = true
return events, nil
}

func (this *Reader) groupToEvents(counters map[string][]pdh.CounterValue) []mb.Event {
func (re *Reader) groupToEvents(counters map[string][]pdh.CounterValue) []mb.Event {
eventMap := make(map[string]*mb.Event)

for counterPath, values := range counters {
for ind, val := range values {
// Some counters, such as rate counters, require two counter values in order to compute a displayable value. In this case we must call PdhCollectQueryData twice before calling PdhGetFormattedCounterValue.
// For more information, see Collecting Performance Data (https://docs.microsoft.com/en-us/windows/desktop/PerfCtrs/collecting-performance-data).
if val.Err != nil && !this.executed {
this.log.Debugw("Ignoring the first measurement because the data isn't ready",
if val.Err != nil && !re.executed {
re.log.Debugw("Ignoring the first measurement because the data isn't ready",
"error", val.Err, logp.Namespace("perfmon"), "query", counterPath)
continue
}

var eventKey string
if this.config.GroupMeasurements && val.Err == nil {
if re.config.GroupMeasurements && val.Err == nil {
// Send measurements with the same instance label as part of the same event
eventKey = val.Instance
} else {
Expand All @@ -195,20 +190,20 @@ func (this *Reader) groupToEvents(counters map[string][]pdh.CounterValue) []mb.E
MetricSetFields: common.MapStr{},
Error: errors.Wrapf(val.Err, "failed on query=%v", counterPath),
}
if val.Instance != "" && this.instanceLabel[counterPath] != "" {
if val.Instance != "" && re.instanceLabel[counterPath] != "" {
//will ignore instance counter
if ok, match := matchesParentProcess(val.Instance); ok {
eventMap[eventKey].MetricSetFields.Put(this.instanceLabel[counterPath], match)
eventMap[eventKey].MetricSetFields.Put(re.instanceLabel[counterPath], match)
} else {
eventMap[eventKey].MetricSetFields.Put(this.instanceLabel[counterPath], val.Instance)
eventMap[eventKey].MetricSetFields.Put(re.instanceLabel[counterPath], val.Instance)
}
}
}
event := eventMap[eventKey]
if val.Measurement != nil {
event.MetricSetFields.Put(this.measurement[counterPath], val.Measurement)
event.MetricSetFields.Put(re.measurement[counterPath], val.Measurement)
} else {
event.MetricSetFields.Put(this.measurement[counterPath], 0)
event.MetricSetFields.Put(re.measurement[counterPath], 0)
}
}
}
Expand All @@ -220,7 +215,7 @@ func (this *Reader) groupToEvents(counters map[string][]pdh.CounterValue) []mb.E
return events
}

func (this *Reader) groupToEvent(counters map[string][]pdh.CounterValue) mb.Event {
func (re *Reader) groupToEvent(counters map[string][]pdh.CounterValue) mb.Event {
event := mb.Event{
MetricSetFields: common.MapStr{},
}
Expand All @@ -229,18 +224,18 @@ func (this *Reader) groupToEvent(counters map[string][]pdh.CounterValue) mb.Even
for _, val := range values {
// Some counters, such as rate counters, require two counter values in order to compute a displayable value. In this case we must call PdhCollectQueryData twice before calling PdhGetFormattedCounterValue.
// For more information, see Collecting Performance Data (https://docs.microsoft.com/en-us/windows/desktop/PerfCtrs/collecting-performance-data).
if val.Err != nil && !this.executed {
this.log.Debugw("Ignoring the first measurement because the data isn't ready",
if val.Err != nil && !re.executed {
re.log.Debugw("Ignoring the first measurement because the data isn't ready",
"error", val.Err, logp.Namespace("perfmon"), "query", counterPath)
continue
}

if _, ok := measurements[this.measurement[counterPath]]; !ok {
measurements[this.measurement[counterPath]] = val.Measurement.(float64)
measurements[this.measurement[counterPath]+instanceCountLabel] = 1
if _, ok := measurements[re.measurement[counterPath]]; !ok {
measurements[re.measurement[counterPath]] = val.Measurement.(float64)
measurements[re.measurement[counterPath]+instanceCountLabel] = 1
} else {
measurements[this.measurement[counterPath]+instanceCountLabel] = measurements[this.measurement[counterPath]+instanceCountLabel] + 1
measurements[this.measurement[counterPath]] = measurements[this.measurement[counterPath]] + val.Measurement.(float64)
measurements[re.measurement[counterPath]+instanceCountLabel] = measurements[re.measurement[counterPath]+instanceCountLabel] + 1
measurements[re.measurement[counterPath]] = measurements[re.measurement[counterPath]] + val.Measurement.(float64)
}
}
}
Expand All @@ -249,7 +244,7 @@ func (this *Reader) groupToEvent(counters map[string][]pdh.CounterValue) mb.Even
if val == 1 {
continue
} else {
event.MetricSetFields.Put(fmt.Sprintf("%s.%s", strings.Split(key, ".")[0], this.config.GroupAllCountersTo), val)
event.MetricSetFields.Put(fmt.Sprintf("%s.%s", strings.Split(key, ".")[0], re.config.GroupAllCountersTo), val)
}
} else {
event.MetricSetFields.Put(key, val)
Expand All @@ -259,8 +254,8 @@ func (this *Reader) groupToEvent(counters map[string][]pdh.CounterValue) mb.Even
}

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

// matchParentProcess will try to get the parent process name
Expand All @@ -271,27 +266,3 @@ func matchesParentProcess(instanceName string) (bool, string) {
}
return false, instanceName
}

// filterValuesByInstances func filters the counter valus based on the instances provided by the user, this is applied to the 'website' metricset at the moment so the _Total instance should be filtered out
func filterValuesByInstances(instances []string, counterValues map[string][]pdh.CounterValue) map[string][]pdh.CounterValue {
filteredValues := make(map[string][]pdh.CounterValue)
for key, values := range counterValues {
filteredValues[key] = []pdh.CounterValue{}
for _, value := range values {
if containsInstance(value, instances) {
filteredValues[key] = append(filteredValues[key], value)
}
}
}
return filteredValues
}

// containsInstance func checks if the counter value contains a filtered instance
func containsInstance(counterValue pdh.CounterValue, array []string) bool {
for _, ins := range array {
if ins == counterValue.Instance {
return true
}
}
return false
}