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

New module windows for metricbeat #3758

Merged
merged 18 commits into from Mar 28, 2017
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Binary file added libbeat/dashboards/import_dashboards
Binary file not shown.
2 changes: 2 additions & 0 deletions metricbeat/include/list.go
Expand Up @@ -69,6 +69,8 @@ import (
_ "github.com/elastic/beats/metricbeat/module/system/network"
_ "github.com/elastic/beats/metricbeat/module/system/process"
_ "github.com/elastic/beats/metricbeat/module/system/socket"
_ "github.com/elastic/beats/metricbeat/module/windows"
_ "github.com/elastic/beats/metricbeat/module/windows/perfmon"
_ "github.com/elastic/beats/metricbeat/module/zookeeper"
_ "github.com/elastic/beats/metricbeat/module/zookeeper/mntr"
)
13 changes: 13 additions & 0 deletions metricbeat/module/windows/_meta/config.yml
@@ -0,0 +1,13 @@
- module: windows
metricsets: ["perfmon"]
enabled: true
period: 1s
Copy link
Member

Choose a reason for hiding this comment

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

Our default is 10s

counters:
Copy link
Member

Choose a reason for hiding this comment

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

For metricset specific configs we (recently) created the convention to have it under a namespace. Means here it would be perfmon.counters:

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In source i then have to write this config:"perfmon.counters" validate:"required"?

Copy link
Member

Choose a reason for hiding this comment

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

yes

- group: "processor"
collectors:
- alias: "processor_perfomance"
query: "\\Processor Information(_Total)\\% Processor Performance"
Copy link
Member

Choose a reason for hiding this comment

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

If you use single quotes here, can the double backslashes be replaced with a single backslash? I would prefer that, unless of course using double backslashes is common in the perfmon world.

- group: "disk"
collectors:
- alias: "bytes_written"
query: "\\FileSystem Disk Activity(_Total)\\FileSystem Bytes Written"
4 changes: 4 additions & 0 deletions metricbeat/module/windows/_meta/docs.asciidoc
@@ -0,0 +1,4 @@
== windows Module

This is the windows Module.

9 changes: 9 additions & 0 deletions metricbeat/module/windows/_meta/fields.yml
@@ -0,0 +1,9 @@
- key: windows
title: "Windows"
description: >
Copy link
Member

Choose a reason for hiding this comment

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

Module for Windows
fields:
- name: windows
type: group
description: >
fields:
4 changes: 4 additions & 0 deletions metricbeat/module/windows/doc.go
@@ -0,0 +1,4 @@
/*
Package windows is a Metricbeat module that contains MetricSets.
*/
package windows
19 changes: 19 additions & 0 deletions metricbeat/module/windows/perfmon/_meta/data.json
@@ -0,0 +1,19 @@
{
"@timestamp":"2016-05-23T08:05:34.853Z",
"beat":{
"hostname":"beathost",
"name":"beathost"
},
"metricset":{
"host":"localhost",
"module":"mysql",
"name":"status",
"rtt":44269
},
"windows":{
"perfmon":{
"example": "perfmon"
}
},
"type":"metricsets"
}
3 changes: 3 additions & 0 deletions metricbeat/module/windows/perfmon/_meta/docs.asciidoc
@@ -0,0 +1,3 @@
=== windows perfmon MetricSet

This is the perfmon metricset of the module windows.
26 changes: 26 additions & 0 deletions metricbeat/module/windows/perfmon/_meta/fields.yml
@@ -0,0 +1,26 @@
- name: perfmon
Copy link
Member

Choose a reason for hiding this comment

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

For dynamic metricsets, this will change so what we nomrally do is just define windows (module) part and leave the rest empty. Check the jmx metricset as an example.

type: group
fields:
- name: counters
type: group
description: >
Grouping of different counters
fields:
- name: group
type: string
description: >
Name of the group. For example `processor` or `disk`

- name: collectors
type: group
fields:
- name: alias
type: string
description: >
Short form for the query

- name: query
type: string
description: >
The query. For example `\\Processor Information(_Total)\\% Processor Performance`. Backslashes have to be escaped.

31 changes: 31 additions & 0 deletions metricbeat/module/windows/perfmon/defs_windows.go
@@ -0,0 +1,31 @@
// +build ignore

package perfmon

/*
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <pdh.h>
#include <pdhmsg.h>
#cgo LDFLAGS: -lpdh
*/
import "C"

const (
ERROR_SUCCESS = C.ERROR_SUCCESS
PDH_STATUS_VALID_DATA = C.PDH_CSTATUS_VALID_DATA
PDH_STATUS_NEW_DATA = C.PDH_CSTATUS_NEW_DATA
PDH_NO_DATA = C.PDH_NO_DATA
PDH_STATUS_NO_OBJECT = C.PDH_CSTATUS_NO_OBJECT
PDH_STATUS_NO_COUNTER = C.PDH_CSTATUS_NO_COUNTER
PDH_STATUS_INVALID_DATA = C.PDH_CSTATUS_INVALID_DATA
PDH_INVALID_HANDLE = C.PDH_INVALID_HANDLE
PDH_INVALID_DATA = C.PDH_INVALID_DATA
PDH_NO_MORE_DATA = C.PDH_NO_MORE_DATA
PdhFmtDouble = C.PDH_FMT_DOUBLE
PdhFmtLarge = C.PDH_FMT_LARGE
PdhFmtLong = C.PDH_FMT_LONG
)

type PdhCounterValue C.PDH_FMT_COUNTERVALUE
24 changes: 24 additions & 0 deletions metricbeat/module/windows/perfmon/defs_windows_386.go
@@ -0,0 +1,24 @@
package perfmon

const (
ERROR_SUCCESS = 0x0
PDH_STATUS_VALID_DATA = 0x0
PDH_STATUS_NEW_DATA = 0x1
PDH_NO_DATA = 0x800007d5
PDH_STATUS_NO_OBJECT = 0xc0000bb8
PDH_STATUS_NO_COUNTER = 0xc0000bb9
PDH_STATUS_INVALID_DATA = 0xc0000bba
PDH_INVALID_HANDLE = 0xc0000bbc
PDH_INVALID_DATA = 0xc0000bc6
PDH_NO_MORE_DATA = 0xc0000bcc
PdhFmtDouble = 0x00000200
PdhFmtLarge = 0x00000400
PdhFmtLong = 0x00000100
)

type PdhCounterValue struct {
CStatus uint32
Pad_cgo_0 [4]byte
LongValue int32
Pad_cgo_1 [4]byte
}
24 changes: 24 additions & 0 deletions metricbeat/module/windows/perfmon/defs_windows_amd64.go
@@ -0,0 +1,24 @@
package perfmon

const (
ERROR_SUCCESS = 0x0
PDH_STATUS_VALID_DATA = 0x0
PDH_STATUS_NEW_DATA = 0x1
PDH_NO_DATA = 0x800007d5
PDH_STATUS_NO_OBJECT = 0xc0000bb8
PDH_STATUS_NO_COUNTER = 0xc0000bb9
PDH_STATUS_INVALID_DATA = 0xc0000bba
PDH_INVALID_HANDLE = 0xc0000bbc
PDH_INVALID_DATA = 0xc0000bc6
PDH_NO_MORE_DATA = 0xc0000bcc
PdhFmtDouble = 0x00000200
PdhFmtLarge = 0x00000400
PdhFmtLong = 0x00000100
)

type PdhCounterValue struct {
CStatus uint32
Pad_cgo_0 [4]byte
LongValue int32
Pad_cgo_1 [4]byte
}
83 changes: 83 additions & 0 deletions metricbeat/module/windows/perfmon/pdh.go
@@ -0,0 +1,83 @@
package perfmon
Copy link
Member

Choose a reason for hiding this comment

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

You can use // +build windows to make files like this compile only for Windows. Or you can rename them to include a _windows.go suffix. This behavior is described here: https://golang.org/pkg/go/build/


import (
"unsafe"

"github.com/elastic/beats/libbeat/common"
)

type Handle struct {
Status error
Query uintptr
CounterType int
Counters []CounterGroup
}

type CounterGroup struct {
GroupName string
Counters []Counter
}

type Counter struct {
CounterName string
Counter uintptr
CounterPath string
DisplayValue PdhCounterValue
}

func getHandle(config []CounterConfig) (*Handle, int) {
q := &Handle{}
err := _PdhOpenQuery(0, 0, &q.Query)
if err != ERROR_SUCCESS {
return nil, err
}

counterGroups := make([]CounterGroup, len(config))
q.Counters = counterGroups

for i, v := range config {
counterGroups[i] = CounterGroup{GroupName: v.Name, Counters: make([]Counter, len(v.Group))}
for j, v1 := range v.Group {
counterGroups[i].Counters[j] = Counter{CounterName: v1.Alias, CounterPath: v1.Query}
err := _PdhAddCounter(q.Query, counterGroups[i].Counters[j].CounterPath, 0, &counterGroups[i].Counters[j].Counter)
if err != ERROR_SUCCESS {
return q, err
}
}
}

return q, 0
}

func (q *Handle) readData() (common.MapStr, int) {

err := _PdhCollectQueryData(q.Query)

if err != ERROR_SUCCESS {
return nil, err
}

result := common.MapStr{}

for _, v := range q.Counters {
groupVal := make(map[string]interface{})
for _, v1 := range v.Counters {
err := _PdhGetFormattedCounterValue(v1.Counter, PdhFmtDouble, q.CounterType, &v1.DisplayValue)
if err != ERROR_SUCCESS {
return nil, err
}
doubleValue := (*float64)(unsafe.Pointer(&v1.DisplayValue.LongValue))
groupVal[v1.CounterName] = *doubleValue

}
result[v.GroupName] = groupVal
}
return result, 0
}

//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output pdh_windows.go pdh.go
// Windows API calls
//sys _PdhOpenQuery(dataSource uintptr, userData uintptr, query *uintptr) (err int) = pdh.PdhOpenQuery
Copy link
Member

Choose a reason for hiding this comment

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

I recommend changing the method signature of all these functions to return (err error). The newly generated code will then check the int return value and return the appropriate error. This make all the code that uses theses function conform to the standard Go conventions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have change the signature to error. Now it generate this function

func _PdhOpenQuery(dataSource uintptr, userData uintptr, query *uintptr) (err error) {
	r1, _, e1 := syscall.Syscall(procPdhOpenQuery.Addr(), 3, uintptr(dataSource), uintptr(userData), uintptr(unsafe.Pointer(query)))
	if r1 == 0 {
		if e1 != 0 {
			err = error(e1)
		} else {
			err = syscall.EINVAL
		}
	}
	return
}

Now i always get Invalid argument error. How should i handle this error. Is it not better to return nil?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

//sys _PdhAddCounter(query uintptr, counterPath string, userData uintptr, counter *uintptr) (err int) = pdh.PdhAddEnglishCounterW
//sys _PdhCollectQueryData(query uintptr) (err int) = pdh.PdhCollectQueryData
//sys _PdhGetFormattedCounterValue(counter uintptr, format uint32, counterType int, value *PdhCounterValue) (err int) = pdh.PdhGetFormattedCounterValue
52 changes: 52 additions & 0 deletions metricbeat/module/windows/perfmon/pdh_windows.go
@@ -0,0 +1,52 @@
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT

package perfmon

import (
"syscall"
"unsafe"
)

var _ unsafe.Pointer

var (
modpdh = syscall.NewLazyDLL("pdh.dll")

procPdhOpenQuery = modpdh.NewProc("PdhOpenQuery")
procPdhAddEnglishCounterW = modpdh.NewProc("PdhAddEnglishCounterW")
procPdhCollectQueryData = modpdh.NewProc("PdhCollectQueryData")
procPdhGetFormattedCounterValue = modpdh.NewProc("PdhGetFormattedCounterValue")
)

func _PdhOpenQuery(dataSource uintptr, userData uintptr, query *uintptr) (err int) {
r0, _, _ := syscall.Syscall(procPdhOpenQuery.Addr(), 3, uintptr(dataSource), uintptr(userData), uintptr(unsafe.Pointer(query)))
err = int(r0)
return
}

func _PdhAddCounter(query uintptr, counterPath string, userData uintptr, counter *uintptr) (err int) {
var _p0 *uint16
_p0, e := syscall.UTF16PtrFromString(counterPath)
if e != nil {
return
}
return __PdhAddCounter(query, _p0, userData, counter)
}

func __PdhAddCounter(query uintptr, counterPath *uint16, userData uintptr, counter *uintptr) (err int) {
r0, _, _ := syscall.Syscall6(procPdhAddEnglishCounterW.Addr(), 4, uintptr(query), uintptr(unsafe.Pointer(counterPath)), uintptr(userData), uintptr(unsafe.Pointer(counter)), 0, 0)
err = int(r0)
return
}

func _PdhCollectQueryData(query uintptr) (err int) {
r0, _, _ := syscall.Syscall(procPdhCollectQueryData.Addr(), 1, uintptr(query), 0, 0)
err = int(r0)
return
}

func _PdhGetFormattedCounterValue(counter uintptr, format uint32, counterType int, value *PdhCounterValue) (err int) {
r0, _, _ := syscall.Syscall6(procPdhGetFormattedCounterValue.Addr(), 4, uintptr(counter), uintptr(format), uintptr(counterType), uintptr(unsafe.Pointer(value)), 0, 0)
err = int(r0)
return
}