-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Changes from 8 commits
43ee390
569e7f4
a02bdc9
331a56f
30a1966
d287fa9
7e6b88c
9047b36
8ab1da4
793bc1b
24aa90d
6c01c1b
251bc9c
f670912
267bf17
88b0a79
6cadedf
dd2b539
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#- module: windows | ||
# metricsets: ["perfmon"] | ||
# enabled: true | ||
# period: 10s | ||
# perfmon.counters: |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
== windows Module | ||
|
||
This is the windows Module. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
- key: windows | ||
title: "Windows" | ||
description: > | ||
[]beta | ||
Module for Windows | ||
fields: | ||
- name: windows | ||
type: group | ||
description: > | ||
fields: |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/* | ||
Package windows is a Metricbeat module that contains MetricSets. | ||
*/ | ||
package windows |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
=== windows perfmon MetricSet | ||
|
||
This is the perfmon metricset of the module windows. | ||
|
||
[float] | ||
=== Features and configuration | ||
|
||
Metricset to collect performance counters. | ||
Example configuration: | ||
``` | ||
- module: windows | ||
metricsets: ["perfmon"] | ||
enabled: true | ||
period: 10s | ||
perfmon.counters: | ||
- group: "processor" | ||
collectors: | ||
- alias: "processor_perfomance" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for sharing the event output: #3758 (comment) Based on the output I was thinking if we should this Do we need the "group" part, or could we just let the user define Other thing I'm thinking about is how far we can "align" the configs of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I need the group param to get the name. See here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as I understand the code, the group name is only needed to create the Event but not for the API request to fetch the event. If you use So my question is if it would also work, if the config would look as following:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's right. Group name is only for the event. To added to the alias field would be an option There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But you are right, it looks to complicated. I remove the field and built the event with the alias. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is definitively an issue with my suggestion. The benefit is that the user as the full flexibility on where to put events and groups are not strictly required. Also there is less indentation yaml mess that can happen. If it is natural to have a group for the events and users kind of expect that in the result your approach definitively feels more natural. @andrewkroh Any thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would let you decide which is better because of your experience. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm OK with the current config approach. But I haven't tried adding additional counters yet. Let's get some people using it and see what kind of feedback we get. |
||
query: '\Processor Information(_Total)\% Processor Performance' | ||
- alias: "processor_time" | ||
query: "\\Processor Information(_Total)\\% Performance Limit" | ||
- group: "disk" | ||
collectors: | ||
- alias: "bytes_written" | ||
query: '\FileSystem Disk Activity(_Total)\FileSystem Bytes Written' | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
- name: perfmon | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
// +build windows | ||
|
||
package perfmon | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can use |
||
|
||
import ( | ||
"unsafe" | ||
|
||
"time" | ||
|
||
"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, error) { | ||
q := &Handle{} | ||
err := _PdhOpenQuery(0, 0, &q.query) | ||
if err != nil { | ||
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 != nil { | ||
return q, err | ||
} | ||
} | ||
} | ||
|
||
return q, nil | ||
} | ||
|
||
func (q *Handle) readData(firstFetch bool) (common.MapStr, error) { | ||
|
||
err := _PdhCollectQueryData(q.query) | ||
|
||
if firstFetch { | ||
// Most counters require two sample values in order to compute a displayable value. So wait and then collect the second value | ||
time.Sleep(2000) | ||
err = _PdhCollectQueryData(q.query) | ||
} | ||
|
||
if err != nil { | ||
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 != nil { | ||
return nil, err | ||
} | ||
doubleValue := (*float64)(unsafe.Pointer(&v1.displayValue.LongValue)) | ||
groupVal[v1.counterName] = *doubleValue | ||
|
||
} | ||
result[v.GroupName] = groupVal | ||
} | ||
return result, nil | ||
} | ||
|
||
//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 error) = pdh.PdhOpenQuery | ||
//sys _PdhAddCounter(query uintptr, counterPath string, userData uintptr, counter *uintptr) (err error) = pdh.PdhAddEnglishCounterW | ||
//sys _PdhCollectQueryData(query uintptr) (err error) = pdh.PdhCollectQueryData | ||
//sys _PdhGetFormattedCounterValue(counter uintptr, format uint32, counterType int, value *PdhCounterValue) (err error) = pdh.PdhGetFormattedCounterValue |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// 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 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 = nil | ||
} | ||
} | ||
return | ||
} | ||
|
||
func _PdhAddCounter(query uintptr, counterPath string, userData uintptr, counter *uintptr) (err error) { | ||
var _p0 *uint16 | ||
_p0, err = syscall.UTF16PtrFromString(counterPath) | ||
if err != nil { | ||
return | ||
} | ||
return __PdhAddCounter(query, _p0, userData, counter) | ||
} | ||
|
||
func __PdhAddCounter(query uintptr, counterPath *uint16, userData uintptr, counter *uintptr) (err error) { | ||
r1, _, e1 := syscall.Syscall6(procPdhAddEnglishCounterW.Addr(), 4, uintptr(query), uintptr(unsafe.Pointer(counterPath)), uintptr(userData), uintptr(unsafe.Pointer(counter)), 0, 0) | ||
if r1 == 0 { | ||
// Ignoring error 2147485649. This error means PDH_CSTATUS_NO_INSTANCE. See here for description https://msdn.microsoft.com/en-us/library/windows/desktop/aa371894%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 | ||
if e1 != 0 && e1 != 2147485649 { | ||
err = error(e1) | ||
} else { | ||
err = nil | ||
} | ||
} | ||
return | ||
} | ||
|
||
func _PdhCollectQueryData(query uintptr) (err error) { | ||
r1, _, e1 := syscall.Syscall(procPdhCollectQueryData.Addr(), 1, uintptr(query), 0, 0) | ||
if r1 == 0 { | ||
if e1 != 0 { | ||
err = error(e1) | ||
} else { | ||
err = nil | ||
} | ||
} | ||
return | ||
} | ||
|
||
func _PdhGetFormattedCounterValue(counter uintptr, format uint32, counterType int, value *PdhCounterValue) (err error) { | ||
r1, _, e1 := syscall.Syscall6(procPdhGetFormattedCounterValue.Addr(), 4, uintptr(counter), uintptr(format), uintptr(counterType), uintptr(unsafe.Pointer(value)), 0, 0) | ||
if r1 == 0 { | ||
if e1 != 0 { | ||
err = error(e1) | ||
} else { | ||
err = nil | ||
} | ||
} | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please mark this as beta: https://github.com/elastic/beats/blob/master/metricbeat/module/jolokia/_meta/fields.yml#L4