2021-08-24 04:37:44 +08:00
|
|
|
//go:build windows
|
2016-01-23 06:02:21 +08:00
|
|
|
// +build windows
|
|
|
|
|
|
|
|
|
|
package win_perf_counters
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2018-05-25 09:25:06 +08:00
|
|
|
"strings"
|
|
|
|
|
"time"
|
2018-06-12 21:20:00 +08:00
|
|
|
|
|
|
|
|
"github.com/influxdata/telegraf"
|
2021-04-10 01:15:04 +08:00
|
|
|
"github.com/influxdata/telegraf/config"
|
2018-06-12 21:20:00 +08:00
|
|
|
"github.com/influxdata/telegraf/plugins/inputs"
|
2016-01-23 06:02:21 +08:00
|
|
|
)
|
|
|
|
|
|
2017-04-21 02:22:44 +08:00
|
|
|
var sampleConfig = `
|
2016-02-19 05:26:51 +08:00
|
|
|
## By default this plugin returns basic CPU and Disk statistics.
|
|
|
|
|
## See the README file for more examples.
|
|
|
|
|
## Uncomment examples below or write your own as you see fit. If the system
|
|
|
|
|
## being polled for data does not have the Object at startup of the Telegraf
|
|
|
|
|
## agent, it will not be gathered.
|
|
|
|
|
## Settings:
|
2016-01-23 06:02:21 +08:00
|
|
|
# PrintValid = false # Print All matching performance counters
|
2018-06-30 10:01:28 +08:00
|
|
|
# Whether request a timestamp along with the PerfCounter data or just use current time
|
|
|
|
|
# UsePerfCounterTime=true
|
2018-06-12 21:20:00 +08:00
|
|
|
# If UseWildcardsExpansion params is set to true, wildcards (partial wildcards in instance names and wildcards in counters names) in configured counter paths will be expanded
|
2018-06-12 02:10:53 +08:00
|
|
|
# and in case of localized Windows, counter paths will be also localized. It also returns instance indexes in instance names.
|
2018-06-12 21:20:00 +08:00
|
|
|
# If false, wildcards (not partial) in instance names will still be expanded, but instance indexes will not be returned in instance names.
|
2018-06-12 02:10:53 +08:00
|
|
|
#UseWildcardsExpansion = false
|
2021-11-24 06:11:00 +08:00
|
|
|
# When running on a localized version of Windows and with UseWildcardsExpansion = true, Windows will
|
|
|
|
|
# localize object and counter names. When LocalizeWildcardsExpansion = false, use the names in object.Counters instead
|
|
|
|
|
# of the localized names. Only Instances can have wildcards in this case. ObjectName and Counters must not have wildcards when this
|
|
|
|
|
# setting is false.
|
|
|
|
|
#LocalizeWildcardsExpansion = true
|
2018-05-25 09:25:06 +08:00
|
|
|
# Period after which counters will be reread from configuration and wildcards in counter paths expanded
|
|
|
|
|
CountersRefreshInterval="1m"
|
2022-02-02 06:06:44 +08:00
|
|
|
## Accepts a list of PDH error codes which are defined in pdh.go, if this error is encountered it will be ignored
|
|
|
|
|
## For example, you can provide "PDH_NO_DATA" to ignore performance counters with no instances
|
|
|
|
|
## By default no errors are ignored
|
|
|
|
|
## You can find the list here: https://github.com/influxdata/telegraf/blob/master/plugins/inputs/win_perf_counters/pdh.go
|
|
|
|
|
## e.g.: IgnoredErrors = ["PDH_NO_DATA"]
|
|
|
|
|
# IgnoredErrors = []
|
2016-01-23 06:02:21 +08:00
|
|
|
|
|
|
|
|
[[inputs.win_perf_counters.object]]
|
|
|
|
|
# Processor usage, alternative to native, reports on a per core.
|
|
|
|
|
ObjectName = "Processor"
|
|
|
|
|
Instances = ["*"]
|
|
|
|
|
Counters = [
|
2019-08-21 08:12:21 +08:00
|
|
|
"% Idle Time",
|
|
|
|
|
"% Interrupt Time",
|
|
|
|
|
"% Privileged Time",
|
|
|
|
|
"% User Time",
|
|
|
|
|
"% Processor Time",
|
|
|
|
|
"% DPC Time",
|
2016-01-23 06:02:21 +08:00
|
|
|
]
|
|
|
|
|
Measurement = "win_cpu"
|
|
|
|
|
# Set to true to include _Total instance when querying for all (*).
|
|
|
|
|
# IncludeTotal=false
|
|
|
|
|
# Print out when the performance counter is missing from object, counter or instance.
|
|
|
|
|
# WarnOnMissing = false
|
2022-02-11 06:03:52 +08:00
|
|
|
# Gather raw values instead of formatted. Raw value is stored in the field name with the "_Raw" suffix, e.g. "Disk_Read_Bytes_sec_Raw".
|
|
|
|
|
# UseRawValues = true
|
2016-01-23 06:02:21 +08:00
|
|
|
|
|
|
|
|
[[inputs.win_perf_counters.object]]
|
|
|
|
|
# Disk times and queues
|
|
|
|
|
ObjectName = "LogicalDisk"
|
|
|
|
|
Instances = ["*"]
|
|
|
|
|
Counters = [
|
2019-08-21 08:12:21 +08:00
|
|
|
"% Idle Time",
|
|
|
|
|
"% Disk Time",
|
|
|
|
|
"% Disk Read Time",
|
|
|
|
|
"% Disk Write Time",
|
|
|
|
|
"% User Time",
|
|
|
|
|
"% Free Space",
|
|
|
|
|
"Current Disk Queue Length",
|
|
|
|
|
"Free Megabytes",
|
2016-01-23 06:02:21 +08:00
|
|
|
]
|
|
|
|
|
Measurement = "win_disk"
|
|
|
|
|
|
2019-08-21 08:12:21 +08:00
|
|
|
[[inputs.win_perf_counters.object]]
|
|
|
|
|
ObjectName = "PhysicalDisk"
|
|
|
|
|
Instances = ["*"]
|
|
|
|
|
Counters = [
|
|
|
|
|
"Disk Read Bytes/sec",
|
|
|
|
|
"Disk Write Bytes/sec",
|
|
|
|
|
"Current Disk Queue Length",
|
|
|
|
|
"Disk Reads/sec",
|
|
|
|
|
"Disk Writes/sec",
|
|
|
|
|
"% Disk Time",
|
|
|
|
|
"% Disk Read Time",
|
|
|
|
|
"% Disk Write Time",
|
|
|
|
|
]
|
|
|
|
|
Measurement = "win_diskio"
|
|
|
|
|
|
|
|
|
|
[[inputs.win_perf_counters.object]]
|
|
|
|
|
ObjectName = "Network Interface"
|
|
|
|
|
Instances = ["*"]
|
|
|
|
|
Counters = [
|
|
|
|
|
"Bytes Received/sec",
|
|
|
|
|
"Bytes Sent/sec",
|
|
|
|
|
"Packets Received/sec",
|
|
|
|
|
"Packets Sent/sec",
|
|
|
|
|
"Packets Received Discarded",
|
|
|
|
|
"Packets Outbound Discarded",
|
|
|
|
|
"Packets Received Errors",
|
|
|
|
|
"Packets Outbound Errors",
|
|
|
|
|
]
|
|
|
|
|
Measurement = "win_net"
|
|
|
|
|
|
|
|
|
|
|
2016-01-23 06:02:21 +08:00
|
|
|
[[inputs.win_perf_counters.object]]
|
|
|
|
|
ObjectName = "System"
|
2019-08-21 08:12:21 +08:00
|
|
|
Counters = [
|
|
|
|
|
"Context Switches/sec",
|
|
|
|
|
"System Calls/sec",
|
|
|
|
|
"Processor Queue Length",
|
|
|
|
|
"System Up Time",
|
|
|
|
|
]
|
2016-01-23 06:02:21 +08:00
|
|
|
Instances = ["------"]
|
|
|
|
|
Measurement = "win_system"
|
|
|
|
|
|
|
|
|
|
[[inputs.win_perf_counters.object]]
|
2018-05-25 09:25:06 +08:00
|
|
|
# Example counterPath where the Instance portion must be removed to get data back,
|
2016-01-23 06:02:21 +08:00
|
|
|
# such as from the Memory object.
|
|
|
|
|
ObjectName = "Memory"
|
|
|
|
|
Counters = [
|
2019-08-21 08:12:21 +08:00
|
|
|
"Available Bytes",
|
|
|
|
|
"Cache Faults/sec",
|
|
|
|
|
"Demand Zero Faults/sec",
|
|
|
|
|
"Page Faults/sec",
|
|
|
|
|
"Pages/sec",
|
|
|
|
|
"Transition Faults/sec",
|
|
|
|
|
"Pool Nonpaged Bytes",
|
|
|
|
|
"Pool Paged Bytes",
|
|
|
|
|
"Standby Cache Reserve Bytes",
|
|
|
|
|
"Standby Cache Normal Priority Bytes",
|
|
|
|
|
"Standby Cache Core Bytes",
|
2016-01-23 06:02:21 +08:00
|
|
|
]
|
2018-05-25 09:25:06 +08:00
|
|
|
Instances = ["------"] # Use 6 x - to remove the Instance bit from the counterPath.
|
2016-01-23 06:02:21 +08:00
|
|
|
Measurement = "win_mem"
|
2019-08-21 08:12:21 +08:00
|
|
|
|
|
|
|
|
[[inputs.win_perf_counters.object]]
|
|
|
|
|
# Example query where the Instance portion must be removed to get data back,
|
|
|
|
|
# such as from the Paging File object.
|
|
|
|
|
ObjectName = "Paging File"
|
|
|
|
|
Counters = [
|
|
|
|
|
"% Usage",
|
|
|
|
|
]
|
|
|
|
|
Instances = ["_Total"]
|
|
|
|
|
Measurement = "win_swap"
|
2016-01-23 06:02:21 +08:00
|
|
|
`
|
|
|
|
|
|
|
|
|
|
type Win_PerfCounters struct {
|
2022-02-04 00:33:38 +08:00
|
|
|
PrintValid bool `toml:"PrintValid"`
|
|
|
|
|
PreVistaSupport bool `toml:"PreVistaSupport" deprecated:"1.7.0;determined dynamically"`
|
2021-11-24 06:11:00 +08:00
|
|
|
UsePerfCounterTime bool
|
|
|
|
|
Object []perfobject
|
|
|
|
|
CountersRefreshInterval config.Duration
|
|
|
|
|
UseWildcardsExpansion bool
|
|
|
|
|
LocalizeWildcardsExpansion bool
|
2022-02-02 06:06:44 +08:00
|
|
|
IgnoredErrors []string `toml:"IgnoredErrors"`
|
2018-05-25 09:25:06 +08:00
|
|
|
|
2019-09-24 06:39:50 +08:00
|
|
|
Log telegraf.Logger
|
|
|
|
|
|
2018-05-25 09:25:06 +08:00
|
|
|
lastRefreshed time.Time
|
|
|
|
|
counters []*counter
|
|
|
|
|
query PerformanceQuery
|
2016-01-23 06:02:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type perfobject struct {
|
|
|
|
|
ObjectName string
|
|
|
|
|
Counters []string
|
|
|
|
|
Instances []string
|
|
|
|
|
Measurement string
|
|
|
|
|
WarnOnMissing bool
|
|
|
|
|
FailOnMissing bool
|
|
|
|
|
IncludeTotal bool
|
2022-02-11 06:03:52 +08:00
|
|
|
UseRawValues bool
|
2016-01-23 06:02:21 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-25 09:25:06 +08:00
|
|
|
type counter struct {
|
|
|
|
|
counterPath string
|
2016-01-23 06:02:21 +08:00
|
|
|
objectName string
|
|
|
|
|
counter string
|
|
|
|
|
instance string
|
|
|
|
|
measurement string
|
2018-05-25 09:25:06 +08:00
|
|
|
includeTotal bool
|
2022-02-11 06:03:52 +08:00
|
|
|
useRawValue bool
|
2016-11-10 02:43:39 +08:00
|
|
|
counterHandle PDH_HCOUNTER
|
2016-01-23 06:02:21 +08:00
|
|
|
}
|
|
|
|
|
|
2018-06-30 10:01:28 +08:00
|
|
|
type instanceGrouping struct {
|
|
|
|
|
name string
|
|
|
|
|
instance string
|
|
|
|
|
objectname string
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-20 16:24:34 +08:00
|
|
|
var sanitizedChars = strings.NewReplacer("/sec", "_persec", "/Sec", "_persec",
|
|
|
|
|
" ", "_", "%", "Percent", `\`, "")
|
2016-04-29 01:12:04 +08:00
|
|
|
|
2018-09-02 09:59:03 +08:00
|
|
|
// extractCounterInfoFromCounterPath gets object name, instance name (if available) and counter name from counter path
|
|
|
|
|
// General Counter path pattern is: \\computer\object(parent/instance#index)\counter
|
|
|
|
|
// parent/instance#index part is skipped in single instance objects (e.g. Memory): \\computer\object\counter
|
|
|
|
|
func extractCounterInfoFromCounterPath(counterPath string) (object string, instance string, counter string, err error) {
|
|
|
|
|
|
|
|
|
|
rightObjectBorderIndex := -1
|
|
|
|
|
leftObjectBorderIndex := -1
|
|
|
|
|
leftCounterBorderIndex := -1
|
|
|
|
|
rightInstanceBorderIndex := -1
|
|
|
|
|
leftInstanceBorderIndex := -1
|
|
|
|
|
bracketLevel := 0
|
|
|
|
|
|
|
|
|
|
for i := len(counterPath) - 1; i >= 0; i-- {
|
|
|
|
|
switch counterPath[i] {
|
|
|
|
|
case '\\':
|
|
|
|
|
if bracketLevel == 0 {
|
|
|
|
|
if leftCounterBorderIndex == -1 {
|
|
|
|
|
leftCounterBorderIndex = i
|
|
|
|
|
} else if leftObjectBorderIndex == -1 {
|
|
|
|
|
leftObjectBorderIndex = i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case '(':
|
|
|
|
|
bracketLevel--
|
|
|
|
|
if leftInstanceBorderIndex == -1 && bracketLevel == 0 && leftObjectBorderIndex == -1 && leftCounterBorderIndex > -1 {
|
|
|
|
|
leftInstanceBorderIndex = i
|
|
|
|
|
rightObjectBorderIndex = i
|
|
|
|
|
}
|
|
|
|
|
case ')':
|
|
|
|
|
if rightInstanceBorderIndex == -1 && bracketLevel == 0 && leftCounterBorderIndex > -1 {
|
|
|
|
|
rightInstanceBorderIndex = i
|
|
|
|
|
}
|
|
|
|
|
bracketLevel++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if rightObjectBorderIndex == -1 {
|
|
|
|
|
rightObjectBorderIndex = leftCounterBorderIndex
|
|
|
|
|
}
|
|
|
|
|
if rightObjectBorderIndex == -1 || leftObjectBorderIndex == -1 {
|
|
|
|
|
err = errors.New("cannot parse object from: " + counterPath)
|
2018-05-25 09:25:06 +08:00
|
|
|
return
|
2017-01-25 04:46:06 +08:00
|
|
|
}
|
2018-09-02 09:59:03 +08:00
|
|
|
|
|
|
|
|
if leftInstanceBorderIndex > -1 && rightInstanceBorderIndex > -1 {
|
|
|
|
|
instance = counterPath[leftInstanceBorderIndex+1 : rightInstanceBorderIndex]
|
|
|
|
|
} else if (leftInstanceBorderIndex == -1 && rightInstanceBorderIndex > -1) || (leftInstanceBorderIndex > -1 && rightInstanceBorderIndex == -1) {
|
|
|
|
|
err = errors.New("cannot parse instance from: " + counterPath)
|
|
|
|
|
return
|
2018-05-25 09:25:06 +08:00
|
|
|
}
|
2018-09-02 09:59:03 +08:00
|
|
|
object = counterPath[leftObjectBorderIndex+1 : rightObjectBorderIndex]
|
|
|
|
|
counter = counterPath[leftCounterBorderIndex+1:]
|
2018-05-25 09:25:06 +08:00
|
|
|
return
|
2016-01-23 06:02:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *Win_PerfCounters) Description() string {
|
2018-05-25 09:25:06 +08:00
|
|
|
return "Input plugin to counterPath Performance Counters on Windows operating systems"
|
2016-01-23 06:02:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *Win_PerfCounters) SampleConfig() string {
|
|
|
|
|
return sampleConfig
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-11 06:03:52 +08:00
|
|
|
func newCounter(counterHandle PDH_HCOUNTER, counterPath string, objectName string, instance string, counterName string, measurement string, includeTotal bool, useRawValue bool) *counter {
|
|
|
|
|
measurementName := sanitizedChars.Replace(measurement)
|
|
|
|
|
if measurementName == "" {
|
|
|
|
|
measurementName = "win_perf_counters"
|
|
|
|
|
}
|
|
|
|
|
newCounterName := sanitizedChars.Replace(counterName)
|
|
|
|
|
if useRawValue {
|
|
|
|
|
newCounterName += "_Raw"
|
|
|
|
|
}
|
|
|
|
|
return &counter{counterPath, objectName, newCounterName, instance, measurementName,
|
|
|
|
|
includeTotal, useRawValue, counterHandle}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *Win_PerfCounters) AddItem(counterPath string, objectName string, instance string, counterName string, measurement string, includeTotal bool, useRawValue bool) error {
|
2021-11-24 06:11:00 +08:00
|
|
|
origCounterPath := counterPath
|
2018-06-12 02:10:53 +08:00
|
|
|
var err error
|
|
|
|
|
var counterHandle PDH_HCOUNTER
|
2018-06-30 10:01:28 +08:00
|
|
|
if !m.query.IsVistaOrNewer() {
|
2018-06-12 02:10:53 +08:00
|
|
|
counterHandle, err = m.query.AddCounterToQuery(counterPath)
|
2018-05-25 09:25:06 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2018-06-12 02:10:53 +08:00
|
|
|
counterHandle, err = m.query.AddEnglishCounterToQuery(counterPath)
|
2018-05-25 09:25:06 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2018-06-12 02:10:53 +08:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if m.UseWildcardsExpansion {
|
|
|
|
|
origInstance := instance
|
2018-05-25 09:25:06 +08:00
|
|
|
counterPath, err = m.query.GetCounterPath(counterHandle)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2018-06-12 02:10:53 +08:00
|
|
|
counters, err := m.query.ExpandWildCardPath(counterPath)
|
2018-05-25 09:25:06 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-24 06:11:00 +08:00
|
|
|
origObjectName, _, origCounterName, err := extractCounterInfoFromCounterPath(origCounterPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-12 02:10:53 +08:00
|
|
|
for _, counterPath := range counters {
|
|
|
|
|
var err error
|
|
|
|
|
|
2018-09-02 09:59:03 +08:00
|
|
|
objectName, instance, counterName, err = extractCounterInfoFromCounterPath(counterPath)
|
2018-06-12 02:10:53 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-24 06:11:00 +08:00
|
|
|
var newItem *counter
|
|
|
|
|
if !m.LocalizeWildcardsExpansion {
|
|
|
|
|
// On localized installations of Windows, Telegraf
|
|
|
|
|
// should return English metrics, but
|
|
|
|
|
// ExpandWildCardPath returns localized counters. Undo
|
|
|
|
|
// that by using the original object and counter
|
|
|
|
|
// names, along with the expanded instance.
|
|
|
|
|
|
|
|
|
|
var newInstance string
|
|
|
|
|
if instance == "" {
|
|
|
|
|
newInstance = emptyInstance
|
|
|
|
|
} else {
|
|
|
|
|
newInstance = instance
|
|
|
|
|
}
|
|
|
|
|
counterPath = formatPath(origObjectName, newInstance, origCounterName)
|
|
|
|
|
counterHandle, err = m.query.AddEnglishCounterToQuery(counterPath)
|
2022-02-11 06:03:52 +08:00
|
|
|
newItem = newCounter(
|
|
|
|
|
counterHandle,
|
2021-11-24 06:11:00 +08:00
|
|
|
counterPath,
|
2022-02-11 06:03:52 +08:00
|
|
|
origObjectName, instance,
|
|
|
|
|
origCounterName,
|
|
|
|
|
measurement,
|
|
|
|
|
includeTotal,
|
|
|
|
|
useRawValue,
|
|
|
|
|
)
|
2021-11-24 06:11:00 +08:00
|
|
|
} else {
|
|
|
|
|
counterHandle, err = m.query.AddCounterToQuery(counterPath)
|
2022-02-11 06:03:52 +08:00
|
|
|
newItem = newCounter(
|
|
|
|
|
counterHandle,
|
2021-11-24 06:11:00 +08:00
|
|
|
counterPath,
|
2022-02-11 06:03:52 +08:00
|
|
|
objectName,
|
|
|
|
|
instance,
|
|
|
|
|
counterName,
|
|
|
|
|
measurement,
|
|
|
|
|
includeTotal,
|
|
|
|
|
useRawValue,
|
|
|
|
|
)
|
2021-11-24 06:11:00 +08:00
|
|
|
}
|
|
|
|
|
|
2018-06-12 02:10:53 +08:00
|
|
|
if instance == "_Total" && origInstance == "*" && !includeTotal {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m.counters = append(m.counters, newItem)
|
2018-05-25 09:25:06 +08:00
|
|
|
|
2018-06-12 02:10:53 +08:00
|
|
|
if m.PrintValid {
|
2019-09-24 06:39:50 +08:00
|
|
|
m.Log.Infof("Valid: %s", counterPath)
|
2018-06-12 02:10:53 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2022-02-11 06:03:52 +08:00
|
|
|
newItem := newCounter(
|
|
|
|
|
counterHandle,
|
|
|
|
|
counterPath,
|
|
|
|
|
objectName,
|
|
|
|
|
instance,
|
|
|
|
|
counterName,
|
|
|
|
|
measurement,
|
|
|
|
|
includeTotal,
|
|
|
|
|
useRawValue,
|
|
|
|
|
)
|
2018-05-25 09:25:06 +08:00
|
|
|
m.counters = append(m.counters, newItem)
|
|
|
|
|
if m.PrintValid {
|
2019-09-24 06:39:50 +08:00
|
|
|
m.Log.Infof("Valid: %s", counterPath)
|
2018-05-25 09:25:06 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-24 06:11:00 +08:00
|
|
|
const emptyInstance = "------"
|
|
|
|
|
|
|
|
|
|
func formatPath(objectname string, instance string, counter string) string {
|
|
|
|
|
if instance == emptyInstance {
|
|
|
|
|
return "\\" + objectname + "\\" + counter
|
|
|
|
|
} else {
|
|
|
|
|
return "\\" + objectname + "(" + instance + ")\\" + counter
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-23 02:58:00 +08:00
|
|
|
func (m *Win_PerfCounters) ParseConfig() error {
|
2018-05-25 09:25:06 +08:00
|
|
|
var counterPath string
|
2016-01-23 06:02:21 +08:00
|
|
|
|
|
|
|
|
if len(m.Object) > 0 {
|
|
|
|
|
for _, PerfObject := range m.Object {
|
|
|
|
|
for _, counter := range PerfObject.Counters {
|
2022-02-11 06:03:52 +08:00
|
|
|
if len(PerfObject.Instances) == 0 {
|
|
|
|
|
m.Log.Warnf("Missing 'Instances' param for object '%s'\n", PerfObject.ObjectName)
|
|
|
|
|
}
|
2016-01-23 06:02:21 +08:00
|
|
|
for _, instance := range PerfObject.Instances {
|
|
|
|
|
objectname := PerfObject.ObjectName
|
|
|
|
|
|
2021-11-24 06:11:00 +08:00
|
|
|
counterPath = formatPath(objectname, instance, counter)
|
2016-01-23 06:02:21 +08:00
|
|
|
|
2022-02-11 06:03:52 +08:00
|
|
|
err := m.AddItem(counterPath, objectname, instance, counter, PerfObject.Measurement, PerfObject.IncludeTotal, PerfObject.UseRawValues)
|
2016-01-23 06:02:21 +08:00
|
|
|
|
2018-05-25 09:25:06 +08:00
|
|
|
if err != nil {
|
2016-01-31 21:21:24 +08:00
|
|
|
if PerfObject.FailOnMissing || PerfObject.WarnOnMissing {
|
2019-09-24 06:39:50 +08:00
|
|
|
m.Log.Errorf("Invalid counterPath: '%s'. Error: %s\n", counterPath, err.Error())
|
2017-01-25 04:46:06 +08:00
|
|
|
}
|
|
|
|
|
if PerfObject.FailOnMissing {
|
2016-01-31 21:21:24 +08:00
|
|
|
return err
|
|
|
|
|
}
|
2016-01-23 06:02:21 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
} else {
|
2018-05-25 09:25:06 +08:00
|
|
|
err := errors.New("no performance objects configured")
|
2016-01-23 06:02:21 +08:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-01 02:03:26 +08:00
|
|
|
}
|
|
|
|
|
|
2022-02-02 06:06:44 +08:00
|
|
|
func (m *Win_PerfCounters) checkError(err error) error {
|
|
|
|
|
if pdhErr, ok := err.(*PdhError); ok {
|
|
|
|
|
for _, ignoredErrors := range m.IgnoredErrors {
|
|
|
|
|
if PDHErrors[pdhErr.ErrorCode] == ignoredErrors {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-23 06:02:21 +08:00
|
|
|
func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error {
|
2017-05-23 02:58:00 +08:00
|
|
|
// Parse the config once
|
2018-05-25 09:25:06 +08:00
|
|
|
var err error
|
|
|
|
|
|
2021-04-10 01:15:04 +08:00
|
|
|
if m.lastRefreshed.IsZero() || (m.CountersRefreshInterval > 0 && m.lastRefreshed.Add(time.Duration(m.CountersRefreshInterval)).Before(time.Now())) {
|
2018-06-12 02:10:53 +08:00
|
|
|
if m.counters != nil {
|
|
|
|
|
m.counters = m.counters[:0]
|
|
|
|
|
}
|
2018-05-25 09:25:06 +08:00
|
|
|
|
2018-06-30 10:01:28 +08:00
|
|
|
if err = m.query.Open(); err != nil {
|
2016-01-23 06:02:21 +08:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-30 10:01:28 +08:00
|
|
|
if err = m.ParseConfig(); err != nil {
|
2018-05-25 09:25:06 +08:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
//some counters need two data samples before computing a value
|
2018-06-30 10:01:28 +08:00
|
|
|
if err = m.query.CollectData(); err != nil {
|
2022-02-02 06:06:44 +08:00
|
|
|
return m.checkError(err)
|
2018-05-25 09:25:06 +08:00
|
|
|
}
|
|
|
|
|
m.lastRefreshed = time.Now()
|
|
|
|
|
|
|
|
|
|
time.Sleep(time.Second)
|
|
|
|
|
}
|
2016-01-23 06:02:21 +08:00
|
|
|
|
2018-06-30 10:01:28 +08:00
|
|
|
var collectFields = make(map[instanceGrouping]map[string]interface{})
|
2018-05-01 08:48:45 +08:00
|
|
|
|
2018-06-30 10:01:28 +08:00
|
|
|
var timestamp time.Time
|
|
|
|
|
if m.UsePerfCounterTime && m.query.IsVistaOrNewer() {
|
|
|
|
|
timestamp, err = m.query.CollectDataWithTime()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
timestamp = time.Now()
|
|
|
|
|
if err = m.query.CollectData(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2018-05-25 09:25:06 +08:00
|
|
|
}
|
2022-02-11 06:03:52 +08:00
|
|
|
var value interface{}
|
2016-01-23 06:02:21 +08:00
|
|
|
// For iterate over the known metrics and get the samples.
|
2018-05-25 09:25:06 +08:00
|
|
|
for _, metric := range m.counters {
|
2016-01-23 06:02:21 +08:00
|
|
|
// collect
|
2018-06-12 02:10:53 +08:00
|
|
|
if m.UseWildcardsExpansion {
|
2022-02-11 06:03:52 +08:00
|
|
|
if metric.useRawValue {
|
|
|
|
|
value, err = m.query.GetRawCounterValue(metric.counterHandle)
|
|
|
|
|
} else {
|
|
|
|
|
value, err = m.query.GetFormattedCounterValueDouble(metric.counterHandle)
|
|
|
|
|
}
|
2020-10-26 23:06:39 +08:00
|
|
|
if err != nil {
|
2018-06-30 10:01:28 +08:00
|
|
|
//ignore invalid data as some counters from process instances returns this sometimes
|
|
|
|
|
if !isKnownCounterDataError(err) {
|
2018-06-12 02:10:53 +08:00
|
|
|
return fmt.Errorf("error while getting value for counter %s: %v", metric.counterPath, err)
|
|
|
|
|
}
|
2020-10-26 23:06:39 +08:00
|
|
|
m.Log.Warnf("error while getting value for counter %q, will skip metric: %v", metric.counterPath, err)
|
|
|
|
|
continue
|
2018-05-25 09:25:06 +08:00
|
|
|
}
|
2020-10-26 23:06:39 +08:00
|
|
|
addCounterMeasurement(metric, metric.instance, value, collectFields)
|
2018-05-25 09:25:06 +08:00
|
|
|
} else {
|
2022-02-11 06:03:52 +08:00
|
|
|
var counterValues []CounterValue
|
|
|
|
|
if metric.useRawValue {
|
|
|
|
|
counterValues, err = m.query.GetRawCounterArray(metric.counterHandle)
|
|
|
|
|
} else {
|
|
|
|
|
counterValues, err = m.query.GetFormattedCounterArrayDouble(metric.counterHandle)
|
|
|
|
|
}
|
2020-10-26 23:06:39 +08:00
|
|
|
if err != nil {
|
|
|
|
|
//ignore invalid data as some counters from process instances returns this sometimes
|
2018-06-30 10:01:28 +08:00
|
|
|
if !isKnownCounterDataError(err) {
|
|
|
|
|
return fmt.Errorf("error while getting value for counter %s: %v", metric.counterPath, err)
|
|
|
|
|
}
|
2020-10-26 23:06:39 +08:00
|
|
|
m.Log.Warnf("error while getting value for counter %q, will skip metric: %v", metric.counterPath, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
for _, cValue := range counterValues {
|
|
|
|
|
|
|
|
|
|
if strings.Contains(metric.instance, "#") && strings.HasPrefix(metric.instance, cValue.InstanceName) {
|
|
|
|
|
// If you are using a multiple instance identifier such as "w3wp#1"
|
|
|
|
|
// phd.dll returns only the first 2 characters of the identifier.
|
|
|
|
|
cValue.InstanceName = metric.instance
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if shouldIncludeMetric(metric, cValue) {
|
|
|
|
|
addCounterMeasurement(metric, cValue.InstanceName, cValue.Value, collectFields)
|
|
|
|
|
}
|
2016-01-23 06:02:21 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-01 08:48:45 +08:00
|
|
|
for instance, fields := range collectFields {
|
|
|
|
|
var tags = map[string]string{
|
|
|
|
|
"objectname": instance.objectname,
|
|
|
|
|
}
|
2018-05-25 09:25:06 +08:00
|
|
|
if len(instance.instance) > 0 {
|
|
|
|
|
tags["instance"] = instance.instance
|
|
|
|
|
}
|
2018-06-30 10:01:28 +08:00
|
|
|
acc.AddFields(instance.name, fields, tags, timestamp)
|
2018-05-01 08:48:45 +08:00
|
|
|
}
|
|
|
|
|
|
2016-01-23 06:02:21 +08:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-26 23:06:39 +08:00
|
|
|
func shouldIncludeMetric(metric *counter, cValue CounterValue) bool {
|
|
|
|
|
if metric.includeTotal {
|
|
|
|
|
// If IncludeTotal is set, include all.
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if metric.instance == "*" && !strings.Contains(cValue.InstanceName, "_Total") {
|
|
|
|
|
// Catch if set to * and that it is not a '*_Total*' instance.
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if metric.instance == cValue.InstanceName {
|
|
|
|
|
// Catch if we set it to total or some form of it
|
|
|
|
|
return true
|
|
|
|
|
}
|
2021-11-24 06:11:00 +08:00
|
|
|
if metric.instance == emptyInstance {
|
2020-10-26 23:06:39 +08:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-11 06:03:52 +08:00
|
|
|
func addCounterMeasurement(metric *counter, instanceName string, value interface{}, collectFields map[instanceGrouping]map[string]interface{}) {
|
|
|
|
|
var instance = instanceGrouping{metric.measurement, instanceName, metric.objectName}
|
2018-06-30 10:01:28 +08:00
|
|
|
if collectFields[instance] == nil {
|
|
|
|
|
collectFields[instance] = make(map[string]interface{})
|
|
|
|
|
}
|
2022-02-11 06:03:52 +08:00
|
|
|
collectFields[instance][sanitizedChars.Replace(metric.counter)] = value
|
2018-06-30 10:01:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isKnownCounterDataError(err error) bool {
|
|
|
|
|
if pdhErr, ok := err.(*PdhError); ok && (pdhErr.ErrorCode == PDH_INVALID_DATA ||
|
2020-10-26 23:06:39 +08:00
|
|
|
pdhErr.ErrorCode == PDH_CALC_NEGATIVE_DENOMINATOR ||
|
2018-06-30 10:01:28 +08:00
|
|
|
pdhErr.ErrorCode == PDH_CALC_NEGATIVE_VALUE ||
|
2018-12-27 06:51:31 +08:00
|
|
|
pdhErr.ErrorCode == PDH_CSTATUS_INVALID_DATA ||
|
|
|
|
|
pdhErr.ErrorCode == PDH_NO_DATA) {
|
2018-06-30 10:01:28 +08:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-24 06:11:00 +08:00
|
|
|
func (m *Win_PerfCounters) Init() error {
|
|
|
|
|
if m.UseWildcardsExpansion && !m.LocalizeWildcardsExpansion {
|
|
|
|
|
// Counters must not have wildcards with this option
|
|
|
|
|
|
|
|
|
|
found := false
|
|
|
|
|
wildcards := []string{"*", "?"}
|
|
|
|
|
|
|
|
|
|
for _, object := range m.Object {
|
|
|
|
|
for _, wildcard := range wildcards {
|
|
|
|
|
if strings.Contains(object.ObjectName, wildcard) {
|
|
|
|
|
found = true
|
|
|
|
|
m.Log.Errorf("object: %s, contains wildcard %s", object.ObjectName, wildcard)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, counter := range object.Counters {
|
|
|
|
|
for _, wildcard := range wildcards {
|
|
|
|
|
if strings.Contains(counter, wildcard) {
|
|
|
|
|
found = true
|
|
|
|
|
m.Log.Errorf("object: %s, counter: %s contains wildcard %s", object.ObjectName, counter, wildcard)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if found {
|
|
|
|
|
return fmt.Errorf("wildcards can't be used with LocalizeWildcardsExpansion=false")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-23 06:02:21 +08:00
|
|
|
func init() {
|
2018-05-25 09:25:06 +08:00
|
|
|
inputs.Add("win_perf_counters", func() telegraf.Input {
|
2021-11-24 06:11:00 +08:00
|
|
|
return &Win_PerfCounters{
|
|
|
|
|
query: &PerformanceQueryImpl{},
|
|
|
|
|
CountersRefreshInterval: config.Duration(time.Second * 60),
|
|
|
|
|
LocalizeWildcardsExpansion: true,
|
|
|
|
|
}
|
2018-05-25 09:25:06 +08:00
|
|
|
})
|
2016-01-23 06:02:21 +08:00
|
|
|
}
|