Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
726
plugins/inputs/win_perf_counters/README.md
Normal file
726
plugins/inputs/win_perf_counters/README.md
Normal file
|
@ -0,0 +1,726 @@
|
|||
# Windows Performance Counters Input Plugin
|
||||
|
||||
This document presents the input plugin to read Performance Counters on Windows
|
||||
operating systems.
|
||||
|
||||
The configuration is parsed and then tested for validity, such as
|
||||
whether the Object, Instance and Counter exist on Telegraf startup.
|
||||
|
||||
Counter paths are refreshed periodically, see the
|
||||
[CountersRefreshInterval](#countersrefreshinterval) configuration parameter for
|
||||
more info.
|
||||
|
||||
In case of query for all instances `["*"]`, the plugin does not return the
|
||||
instance `_Total` by default. See [IncludeTotal](#includetotal) for more info.
|
||||
|
||||
## Basics
|
||||
|
||||
The examples contained in this file have been found on the internet
|
||||
as counters used when performance monitoring
|
||||
Active Directory and IIS in particular.
|
||||
There are a lot of other good objects to monitor, if you know what to look for.
|
||||
This file is likely to be updated in the future with more examples for
|
||||
useful configurations for separate scenarios.
|
||||
|
||||
For more information on concepts and terminology including object,
|
||||
counter, and instance names, see the help in the Windows Performance
|
||||
Monitor app.
|
||||
|
||||
### Schema
|
||||
|
||||
*Measurement name* is specified per performance object
|
||||
or `win_perf_counters` by default.
|
||||
|
||||
*Tags:*
|
||||
|
||||
- source - computer name, as specified in the `Sources` parameter. Name
|
||||
`localhost` is translated into the host name
|
||||
- objectname - normalized name of the performance object
|
||||
- instance - instance name, if performance object supports multiple instances,
|
||||
otherwise omitted
|
||||
|
||||
*Fields* are counters of the performance object.
|
||||
The field name is normalized counter name.
|
||||
|
||||
### Plugin wide
|
||||
|
||||
Plugin wide entries are underneath `[[inputs.win_perf_counters]]`.
|
||||
|
||||
#### PrintValid
|
||||
|
||||
Bool, if set to `true` will print out all matching performance objects.
|
||||
|
||||
Example:
|
||||
`PrintValid=true`
|
||||
|
||||
#### UseWildcardsExpansion
|
||||
|
||||
If `UseWildcardsExpansion` is true, wildcards can be used in the
|
||||
instance name and the counter name. Instance indexes will also be
|
||||
returned in the instance name.
|
||||
|
||||
Partial wildcards (e.g. `chrome*`) are supported only in the instance
|
||||
name on Windows Vista and newer.
|
||||
|
||||
If disabled, wildcards (not partial) in instance names can still be
|
||||
used, but instance indexes will not be returned in the instance names.
|
||||
|
||||
Example:
|
||||
`UseWildcardsExpansion=true`
|
||||
|
||||
#### LocalizeWildcardsExpansion
|
||||
|
||||
`LocalizeWildcardsExpansion` selects whether object and counter names
|
||||
are localized when `UseWildcardsExpansion` is true and Telegraf is
|
||||
running on a localized installation of Windows.
|
||||
|
||||
When `LocalizeWildcardsExpansion` is true, Telegraf produces metrics
|
||||
with localized tags and fields even when object and counter names are
|
||||
in English.
|
||||
|
||||
When `LocalizeWildcardsExpansion` is false, Telegraf expects object
|
||||
and counter names to be in English and produces metrics with English
|
||||
tags and fields.
|
||||
|
||||
When `LocalizeWildcardsExpansion` is false, wildcards can only be used
|
||||
in instances. Object and counter names must not have wildcards.
|
||||
|
||||
Example:
|
||||
`LocalizeWildcardsExpansion=true`
|
||||
|
||||
#### CountersRefreshInterval
|
||||
|
||||
Configured counters are matched against available counters at the interval
|
||||
specified by the `CountersRefreshInterval` parameter. The default value is `1m`
|
||||
(1 minute).
|
||||
|
||||
If wildcards are used in instance or counter names, they are expanded at this
|
||||
point, if the `UseWildcardsExpansion` param is set to `true`.
|
||||
|
||||
Setting the `CountersRefreshInterval` too low (order of seconds) can cause
|
||||
Telegraf to create a high CPU load.
|
||||
|
||||
Set it to `0s` to disable periodic refreshing.
|
||||
|
||||
Example:
|
||||
`CountersRefreshInterval=1m`
|
||||
|
||||
#### PreVistaSupport
|
||||
|
||||
(Deprecated in 1.7; Necessary features on Windows Vista and newer are checked
|
||||
dynamically)
|
||||
|
||||
Bool, if set to `true`, the plugin will use the localized PerfCounter interface
|
||||
that has been present since before Vista for backwards compatibility.
|
||||
|
||||
It is recommended NOT to use this on OSes starting with Vista and newer because
|
||||
it requires more configuration to use this than the newer interface present
|
||||
since Vista.
|
||||
|
||||
Example for Windows Server 2003, this would be set to true:
|
||||
`PreVistaSupport=true`
|
||||
|
||||
#### UsePerfCounterTime
|
||||
|
||||
Bool, if set to `true` will request a timestamp along with the PerfCounter data.
|
||||
If se to `false`, current time will be used.
|
||||
|
||||
Supported on Windows Vista/Windows Server 2008 and newer
|
||||
Example:
|
||||
`UsePerfCounterTime=true`
|
||||
|
||||
#### IgnoredErrors
|
||||
|
||||
IgnoredErrors 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, but by default
|
||||
no errors are ignored. You can find the list of possible errors here: [PDH
|
||||
errors](pdh.go).
|
||||
|
||||
Example:
|
||||
`IgnoredErrors=["PDH_NO_DATA"]`
|
||||
|
||||
#### Sources
|
||||
|
||||
(Optional)
|
||||
|
||||
Host names or ip addresses of computers to gather all performance counters from.
|
||||
The user running Telegraf must be authenticated to the remote computer(s).
|
||||
E.g. via Windows sharing `net use \\SQL-SERVER-01`.
|
||||
Use either localhost (`"localhost"`) or real local computer name to gather
|
||||
counters also from localhost among other computers. Skip, if gather only from
|
||||
localhost.
|
||||
|
||||
If a performance counter is present only on specific hosts set `Sources` param
|
||||
on the specific counter level configuration to override global (plugin wide)
|
||||
sources.
|
||||
|
||||
Example:
|
||||
`Sources = ["localhost", "SQL-SERVER-01", "SQL-SERVER-02", "SQL-SERVER-03"]`
|
||||
|
||||
Default:
|
||||
`Sources = ["localhost"]`
|
||||
|
||||
### Object
|
||||
|
||||
See Entry below.
|
||||
|
||||
### Entry
|
||||
|
||||
A new configuration entry consists of the TOML header starting with,
|
||||
`[[inputs.win_perf_counters.object]]`.
|
||||
This must follow before other plugin configurations,
|
||||
beneath the main win_perf_counters entry, `[[inputs.win_perf_counters]]`.
|
||||
|
||||
Following this are 3 required key/value pairs and three optional parameters and
|
||||
their usage.
|
||||
|
||||
#### ObjectName
|
||||
|
||||
(Required)
|
||||
|
||||
ObjectName is the Object to query for, like Processor, DirectoryServices,
|
||||
LogicalDisk or similar.
|
||||
|
||||
Example: `ObjectName = "LogicalDisk"`
|
||||
|
||||
#### Instances
|
||||
|
||||
(Required)
|
||||
|
||||
The instances key (this is an array) declares the instances of a counter you
|
||||
would like returned, it can be one or more values.
|
||||
|
||||
Example: `Instances = ["C:","D:","E:"]`
|
||||
|
||||
This will return only for the instances C:, D: and E: where relevant. To get all
|
||||
instances of a Counter, use `["*"]` only. By default any results containing
|
||||
`_Total` are stripped, unless this is specified as the wanted instance.
|
||||
Alternatively see the option `IncludeTotal` below.
|
||||
|
||||
It is also possible to set partial wildcards, eg. `["chrome*"]`, if the
|
||||
`UseWildcardsExpansion` param is set to `true`
|
||||
|
||||
Some Objects do not have instances to select from at all.
|
||||
Here only one option is valid if you want data back,
|
||||
and that is to specify `Instances = ["------"]`.
|
||||
|
||||
#### Counters
|
||||
|
||||
(Required)
|
||||
|
||||
The Counters key (this is an array) declares the counters of the ObjectName
|
||||
you would like returned, it can also be one or more values.
|
||||
|
||||
Example: `Counters = ["% Idle Time", "% Disk Read Time", "% Disk Write Time"]`
|
||||
|
||||
This must be specified for every counter you want the results of, or use
|
||||
`["*"]` for all the counters of the object, if the `UseWildcardsExpansion` param
|
||||
is set to `true`.
|
||||
|
||||
#### Sources (Object)
|
||||
|
||||
(Optional)
|
||||
|
||||
Overrides the [Sources](#sources) global parameter for current performance
|
||||
object. See [Sources](#sources) description for more details.
|
||||
|
||||
#### Measurement
|
||||
|
||||
(Optional)
|
||||
|
||||
This key is optional. If it is not set it will be `win_perf_counters`. In
|
||||
InfluxDB this is the key underneath which the returned data is stored. So for
|
||||
ordering your data in a good manner, this is a good key to set with a value when
|
||||
you want your IIS and Disk results stored separately from Processor results.
|
||||
|
||||
Example: `Measurement = "win_disk"`
|
||||
|
||||
#### UseRawValues
|
||||
|
||||
(Optional)
|
||||
|
||||
This key is optional. It is a simple bool. If set to `true`, counter values
|
||||
will be provided in the raw, integer, form. This is in contrast with the default
|
||||
behavior, where values are returned in a formatted, displayable, form
|
||||
as seen in the Windows Performance Monitor.
|
||||
|
||||
A field representing raw counter value has the `_Raw` suffix. Raw values should
|
||||
be further used in a calculation,
|
||||
e.g. `100-(non_negative_derivative("Percent_Processor_Time_Raw",1s)/100000`
|
||||
Note: Time based counters (i.e. *% Processor Time*) are reported in hundredths
|
||||
of nanoseconds.
|
||||
This key is optional. It is a simple bool.
|
||||
If set to `true`, counter values will be provided in the raw, integer, form.
|
||||
This is in contrast with the default behavior, where values are returned in a
|
||||
formatted, displayable, form as seen in the Windows Performance Monitor.
|
||||
A field representing raw counter value has the `_Raw` suffix.
|
||||
Raw values should be further used in a calculation,
|
||||
e.g. `100-(non_negative_derivative("Percent_Processor_Time_Raw",1s)/100000`
|
||||
Note: Time based counters (i.e. `% Processor Time`)
|
||||
are reported in hundredths of nanoseconds.
|
||||
|
||||
Example: `UseRawValues = true`
|
||||
|
||||
#### IncludeTotal
|
||||
|
||||
(Optional)
|
||||
|
||||
This key is optional. It is a simple bool.
|
||||
If it is not set to true or included it is treated as false.
|
||||
This key only has effect if the Instances key is set to `["*"]`
|
||||
and you would also like all instances containing `_Total` to be returned,
|
||||
like `_Total`, `0,_Total` and so on where applicable
|
||||
(Processor Information is one example).
|
||||
|
||||
#### WarnOnMissing
|
||||
|
||||
(Optional)
|
||||
|
||||
This key is optional. It is a simple bool.
|
||||
If it is not set to true or included it is treated as false.
|
||||
This only has effect on the first execution of the plugin.
|
||||
It will print out any ObjectName/Instance/Counter combinations
|
||||
asked for that do not match. Useful when debugging new configurations.
|
||||
|
||||
#### FailOnMissing
|
||||
|
||||
(Internal)
|
||||
|
||||
This key should not be used. It is for testing purposes only. It is a simple
|
||||
bool. If it is not set to true or included this is treated as false. If this is
|
||||
set to true, the plugin will abort and end prematurely if any of the
|
||||
combinations of ObjectName/Instances/Counters are invalid.
|
||||
|
||||
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
|
||||
|
||||
In addition to the plugin-specific configuration settings, plugins support
|
||||
additional global and plugin configuration settings. These settings are used to
|
||||
modify metrics, tags, and field or create aliases and configure ordering, etc.
|
||||
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||
|
||||
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml @sample.conf
|
||||
# Input plugin to counterPath Performance Counters on Windows operating systems
|
||||
# This plugin ONLY supports Windows
|
||||
[[inputs.win_perf_counters]]
|
||||
## 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.
|
||||
|
||||
## Print All matching performance counters
|
||||
# PrintValid = false
|
||||
|
||||
## Whether request a timestamp along with the PerfCounter data or use current
|
||||
## time
|
||||
# UsePerfCounterTime = true
|
||||
|
||||
## 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 and in case of localized Windows, counter
|
||||
## paths will be also localized. It also returns instance indexes in instance
|
||||
## names. If false, wildcards (not partial) in instance names will still be
|
||||
## expanded, but instance indexes will not be returned in instance names.
|
||||
# UseWildcardsExpansion = false
|
||||
|
||||
## 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
|
||||
|
||||
## Period after which counters will be reread from configuration and
|
||||
## wildcards in counter paths expanded
|
||||
# CountersRefreshInterval="1m"
|
||||
|
||||
## 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 = []
|
||||
|
||||
## Maximum size of the buffer for values returned by the API
|
||||
## Increase this value if you experience "buffer limit reached" errors.
|
||||
# MaxBufferSize = "4MiB"
|
||||
|
||||
## NOTE: Due to the way TOML is parsed, tables must be at the END of the
|
||||
## plugin definition, otherwise additional config options are read as part of
|
||||
## the table
|
||||
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = ""
|
||||
# ObjectName = ""
|
||||
# Instances = [""]
|
||||
# Counters = []
|
||||
## Additional Object Settings
|
||||
## * IncludeTotal: set to true to include _Total instance when querying
|
||||
## for all metrics via '*'
|
||||
## * WarnOnMissing: print out when the performance counter is missing
|
||||
## from object, counter or instance
|
||||
## * UseRawValues: gather raw values instead of formatted. Raw values are
|
||||
## stored in the field name with the "_Raw" suffix, e.g.
|
||||
## "Disk_Read_Bytes_sec_Raw".
|
||||
# IncludeTotal = false
|
||||
# WarnOnMissing = false
|
||||
# UseRawValues = false
|
||||
|
||||
## Processor usage, alternative to native, reports on a per core.
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = "win_cpu"
|
||||
# ObjectName = "Processor"
|
||||
# Instances = ["*"]
|
||||
# UseRawValues = true
|
||||
# Counters = [
|
||||
# "% Idle Time",
|
||||
# "% Interrupt Time",
|
||||
# "% Privileged Time",
|
||||
# "% User Time",
|
||||
# "% Processor Time",
|
||||
# "% DPC Time",
|
||||
# ]
|
||||
|
||||
## Disk times and queues
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = "win_disk"
|
||||
# ObjectName = "LogicalDisk"
|
||||
# Instances = ["*"]
|
||||
# Counters = [
|
||||
# "% Idle Time",
|
||||
# "% Disk Time",
|
||||
# "% Disk Read Time",
|
||||
# "% Disk Write Time",
|
||||
# "% User Time",
|
||||
# "% Free Space",
|
||||
# "Current Disk Queue Length",
|
||||
# "Free Megabytes",
|
||||
# ]
|
||||
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = "win_diskio"
|
||||
# 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",
|
||||
# ]
|
||||
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = "win_net"
|
||||
# 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",
|
||||
# ]
|
||||
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = "win_system"
|
||||
# ObjectName = "System"
|
||||
# Instances = ["------"]
|
||||
# Counters = [
|
||||
# "Context Switches/sec",
|
||||
# "System Calls/sec",
|
||||
# "Processor Queue Length",
|
||||
# "System Up Time",
|
||||
# ]
|
||||
|
||||
## Example counterPath where the Instance portion must be removed to get
|
||||
## data back, such as from the Memory object.
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = "win_mem"
|
||||
# ObjectName = "Memory"
|
||||
## Use 6 x - to remove the Instance bit from the counterPath.
|
||||
# Instances = ["------"]
|
||||
# Counters = [
|
||||
# "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",
|
||||
# ]
|
||||
|
||||
## Example query where the Instance portion must be removed to get data back,
|
||||
## such as from the Paging File object.
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = "win_swap"
|
||||
# ObjectName = "Paging File"
|
||||
# Instances = ["_Total"]
|
||||
# Counters = [
|
||||
# "% Usage",
|
||||
# ]
|
||||
```
|
||||
|
||||
### Generic Queries
|
||||
|
||||
```toml
|
||||
[[inputs.win_perf_counters]]
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# Processor usage, alternative to native, reports on a per core.
|
||||
ObjectName = "Processor"
|
||||
Instances = ["*"]
|
||||
Counters = ["% Idle Time", "% Interrupt Time", "% Privileged Time", "% User Time", "% Processor Time"]
|
||||
Measurement = "win_cpu"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# Disk times and queues
|
||||
ObjectName = "LogicalDisk"
|
||||
Instances = ["*"]
|
||||
Counters = ["% Idle Time", "% Disk Time","% Disk Read Time", "% Disk Write Time", "% User Time", "Current Disk Queue Length"]
|
||||
Measurement = "win_disk"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
ObjectName = "System"
|
||||
Counters = ["Context Switches/sec","System Calls/sec", "Processor Queue Length"]
|
||||
Instances = ["------"]
|
||||
Measurement = "win_system"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# Example query where the Instance portion must be removed to get data back, such as from the Memory object.
|
||||
ObjectName = "Memory"
|
||||
Counters = ["Available Bytes","Cache Faults/sec","Demand Zero Faults/sec","Page Faults/sec","Pages/sec","Transition Faults/sec","Pool Nonpaged Bytes","Pool Paged Bytes"]
|
||||
Instances = ["------"] # Use 6 x - to remove the Instance bit from the query.
|
||||
Measurement = "win_mem"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# more counters for the Network Interface Object can be found at
|
||||
# https://msdn.microsoft.com/en-us/library/ms803962.aspx
|
||||
ObjectName = "Network Interface"
|
||||
Counters = ["Bytes Received/sec","Bytes Sent/sec","Packets Received/sec","Packets Sent/sec"]
|
||||
Instances = ["*"] # Use 6 x - to remove the Instance bit from the query.
|
||||
Measurement = "win_net"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
```
|
||||
|
||||
### Active Directory Domain Controller
|
||||
|
||||
```toml
|
||||
[[inputs.win_perf_counters]]
|
||||
[inputs.win_perf_counters.tags]
|
||||
monitorgroup = "ActiveDirectory"
|
||||
[[inputs.win_perf_counters.object]]
|
||||
ObjectName = "DirectoryServices"
|
||||
Instances = ["*"]
|
||||
Counters = ["Base Searches/sec","Database adds/sec","Database deletes/sec","Database modifys/sec","Database recycles/sec","LDAP Client Sessions","LDAP Searches/sec","LDAP Writes/sec"]
|
||||
Measurement = "win_ad" # Set an alternative measurement to win_perf_counters if wanted.
|
||||
#Instances = [""] # Gathers all instances by default, specify to only gather these
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
ObjectName = "Security System-Wide Statistics"
|
||||
Instances = ["*"]
|
||||
Counters = ["NTLM Authentications","Kerberos Authentications","Digest Authentications"]
|
||||
Measurement = "win_ad"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
ObjectName = "Database"
|
||||
Instances = ["*"]
|
||||
Counters = ["Database Cache % Hit","Database Cache Page Fault Stalls/sec","Database Cache Page Faults/sec","Database Cache Size"]
|
||||
Measurement = "win_db"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
```
|
||||
|
||||
### DFS Namespace + Domain Controllers
|
||||
|
||||
```toml
|
||||
[[inputs.win_perf_counters]]
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# AD, DFS N, Useful if the server hosts a DFS Namespace or is a Domain Controller
|
||||
ObjectName = "DFS Namespace Service Referrals"
|
||||
Instances = ["*"]
|
||||
Counters = ["Requests Processed","Requests Failed","Avg. Response Time"]
|
||||
Measurement = "win_dfsn"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
#WarnOnMissing = false # Print out when the performance counter is missing, either of object, counter or instance.
|
||||
```
|
||||
|
||||
### DFS Replication + Domain Controllers
|
||||
|
||||
```toml
|
||||
[[inputs.win_perf_counters]]
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# AD, DFS R, Useful if the server hosts a DFS Replication folder or is a Domain Controller
|
||||
ObjectName = "DFS Replication Service Volumes"
|
||||
Instances = ["*"]
|
||||
Counters = ["Data Lookups","Database Commits"]
|
||||
Measurement = "win_dfsr"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
#WarnOnMissing = false # Print out when the performance counter is missing, either of object, counter or instance.
|
||||
```
|
||||
|
||||
### DNS Server + Domain Controllers
|
||||
|
||||
```toml
|
||||
[[inputs.win_perf_counters]]
|
||||
[[inputs.win_perf_counters.object]]
|
||||
ObjectName = "DNS"
|
||||
Counters = ["Dynamic Update Received","Dynamic Update Rejected","Recursive Queries","Recursive Queries Failure","Secure Update Failure","Secure Update Received","TCP Query Received","TCP Response Sent","UDP Query Received","UDP Response Sent","Total Query Received","Total Response Sent"]
|
||||
Instances = ["------"]
|
||||
Measurement = "win_dns"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
```
|
||||
|
||||
### IIS / ASP.NET
|
||||
|
||||
```toml
|
||||
[[inputs.win_perf_counters]]
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# HTTP Service request queues in the Kernel before being handed over to User Mode.
|
||||
ObjectName = "HTTP Service Request Queues"
|
||||
Instances = ["*"]
|
||||
Counters = ["CurrentQueueSize","RejectedRequests"]
|
||||
Measurement = "win_http_queues"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# IIS, ASP.NET Applications
|
||||
ObjectName = "ASP.NET Applications"
|
||||
Counters = ["Cache Total Entries","Cache Total Hit Ratio","Cache Total Turnover Rate","Output Cache Entries","Output Cache Hits","Output Cache Hit Ratio","Output Cache Turnover Rate","Compilations Total","Errors Total/Sec","Pipeline Instance Count","Requests Executing","Requests in Application Queue","Requests/Sec"]
|
||||
Instances = ["*"]
|
||||
Measurement = "win_aspnet_app"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# IIS, ASP.NET
|
||||
ObjectName = "ASP.NET"
|
||||
Counters = ["Application Restarts","Request Wait Time","Requests Current","Requests Queued","Requests Rejected"]
|
||||
Instances = ["*"]
|
||||
Measurement = "win_aspnet"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# IIS, Web Service
|
||||
ObjectName = "Web Service"
|
||||
Counters = ["Get Requests/sec","Post Requests/sec","Connection Attempts/sec","Current Connections","ISAPI Extension Requests/sec"]
|
||||
Instances = ["*"]
|
||||
Measurement = "win_websvc"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# Web Service Cache / IIS
|
||||
ObjectName = "Web Service Cache"
|
||||
Counters = ["URI Cache Hits %","Kernel: URI Cache Hits %","File Cache Hits %"]
|
||||
Instances = ["*"]
|
||||
Measurement = "win_websvc_cache"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
```
|
||||
|
||||
### Process
|
||||
|
||||
```toml
|
||||
[[inputs.win_perf_counters]]
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# Process metrics, in this case for IIS only
|
||||
ObjectName = "Process"
|
||||
Counters = ["% Processor Time","Handle Count","Private Bytes","Thread Count","Virtual Bytes","Working Set"]
|
||||
Instances = ["w3wp"]
|
||||
Measurement = "win_proc"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
```
|
||||
|
||||
### .NET Monitoring
|
||||
|
||||
```toml
|
||||
[[inputs.win_perf_counters]]
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# .NET CLR Exceptions, in this case for IIS only
|
||||
ObjectName = ".NET CLR Exceptions"
|
||||
Counters = ["# of Exceps Thrown / sec"]
|
||||
Instances = ["w3wp"]
|
||||
Measurement = "win_dotnet_exceptions"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# .NET CLR Jit, in this case for IIS only
|
||||
ObjectName = ".NET CLR Jit"
|
||||
Counters = ["% Time in Jit","IL Bytes Jitted / sec"]
|
||||
Instances = ["w3wp"]
|
||||
Measurement = "win_dotnet_jit"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# .NET CLR Loading, in this case for IIS only
|
||||
ObjectName = ".NET CLR Loading"
|
||||
Counters = ["% Time Loading"]
|
||||
Instances = ["w3wp"]
|
||||
Measurement = "win_dotnet_loading"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# .NET CLR LocksAndThreads, in this case for IIS only
|
||||
ObjectName = ".NET CLR LocksAndThreads"
|
||||
Counters = ["# of current logical Threads","# of current physical Threads","# of current recognized threads","# of total recognized threads","Queue Length / sec","Total # of Contentions","Current Queue Length"]
|
||||
Instances = ["w3wp"]
|
||||
Measurement = "win_dotnet_locks"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# .NET CLR Memory, in this case for IIS only
|
||||
ObjectName = ".NET CLR Memory"
|
||||
Counters = ["% Time in GC","# Bytes in all Heaps","# Gen 0 Collections","# Gen 1 Collections","# Gen 2 Collections","# Induced GC","Allocated Bytes/sec","Finalization Survivors","Gen 0 heap size","Gen 1 heap size","Gen 2 heap size","Large Object Heap size","# of Pinned Objects"]
|
||||
Instances = ["w3wp"]
|
||||
Measurement = "win_dotnet_mem"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# .NET CLR Security, in this case for IIS only
|
||||
ObjectName = ".NET CLR Security"
|
||||
Counters = ["% Time in RT checks","Stack Walk Depth","Total Runtime Checks"]
|
||||
Instances = ["w3wp"]
|
||||
Measurement = "win_dotnet_security"
|
||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you are getting an error about an invalid counter, use the `typeperf` command
|
||||
to check the counter path on the command line. E.g. `typeperf
|
||||
"Process(chrome*)\% Processor Time"`
|
||||
|
||||
If no metrics are emitted even with the default config, you may need to repair
|
||||
your performance counters.
|
||||
|
||||
1. Launch the Command Prompt as Administrator
|
||||
(right click "Runs As Administrator").
|
||||
1. Drop into the C:\WINDOWS\System32 directory by typing `C:` then
|
||||
`cd \Windows\System32`
|
||||
1. Rebuild your counter values, which may take a few moments so please be
|
||||
patient, by running:
|
||||
|
||||
```batchfile
|
||||
lodctr /r
|
||||
```
|
||||
|
||||
## Metrics
|
||||
|
||||
## Example Output
|
56
plugins/inputs/win_perf_counters/kernel32.go
Normal file
56
plugins/inputs/win_perf_counters/kernel32.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2010 The win Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This is the official list of 'win' authors for copyright purposes.
|
||||
//
|
||||
// Alexander Neumann <an2048@googlemail.com>
|
||||
// Joseph Watson <jtwatson@linux-consulting.us>
|
||||
// Kevin Pors <krpors@gmail.com>
|
||||
|
||||
//go:build windows
|
||||
|
||||
package win_perf_counters
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type fileTime struct {
|
||||
dwLowDateTime uint32
|
||||
dwHighDateTime uint32
|
||||
}
|
||||
|
||||
var (
|
||||
// Library
|
||||
libKernelDll *syscall.DLL
|
||||
|
||||
// Functions
|
||||
kernelLocalFileTimeToFileTime *syscall.Proc
|
||||
)
|
||||
|
||||
func init() {
|
||||
libKernelDll = syscall.MustLoadDLL("Kernel32.dll")
|
||||
|
||||
kernelLocalFileTimeToFileTime = libKernelDll.MustFindProc("LocalFileTimeToFileTime")
|
||||
}
|
636
plugins/inputs/win_perf_counters/pdh.go
Normal file
636
plugins/inputs/win_perf_counters/pdh.go
Normal file
|
@ -0,0 +1,636 @@
|
|||
// Copyright (c) 2010 The win Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This is the official list of 'win' authors for copyright purposes.
|
||||
//
|
||||
// Alexander Neumann <an2048@googlemail.com>
|
||||
// Joseph Watson <jtwatson@linux-consulting.us>
|
||||
// Kevin Pors <krpors@gmail.com>
|
||||
|
||||
//go:build windows
|
||||
|
||||
package win_perf_counters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// Error codes
|
||||
const (
|
||||
errorSuccess = 0
|
||||
errorFailure = 1
|
||||
errorInvalidFunction = 1
|
||||
epochDifferenceMicros int64 = 11644473600000000
|
||||
)
|
||||
|
||||
type (
|
||||
handle uintptr
|
||||
)
|
||||
|
||||
// PDH error codes, which can be returned by all Pdh* functions. Taken from mingw-w64 pdhmsg.h
|
||||
const (
|
||||
pdhCstatusValidData = 0x00000000 // The returned data is valid.
|
||||
pdhCstatusNewData = 0x00000001 // The return data value is valid and different from the last sample.
|
||||
pdhCstatusNoMachine = 0x800007D0 // Unable to connect to the specified computer, or the computer is offline.
|
||||
pdhCstatusNoInstance = 0x800007D1
|
||||
pdhMoreData = 0x800007D2 // The pdhGetFormattedCounterArray* function can return this if there's 'more data to be displayed'.
|
||||
pdhCstatusItemNotValidated = 0x800007D3
|
||||
pdhRetry = 0x800007D4
|
||||
pdhNoData = 0x800007D5 // The query does not currently contain any counters (for example, limited access)
|
||||
pdhCalcNegativeDenominator = 0x800007D6
|
||||
pdhCalcNegativeTimebase = 0x800007D7
|
||||
pdhCalcNegativeValue = 0x800007D8
|
||||
pdhDialogCancelled = 0x800007D9
|
||||
pdhEndOfLogFile = 0x800007DA
|
||||
pdhAsyncQueryTimeout = 0x800007DB
|
||||
pdhCannotSetDefaultRealtimeDatasource = 0x800007DC
|
||||
pdhCstatusNoObject = 0xC0000BB8
|
||||
pdhCstatusNoCounter = 0xC0000BB9 // The specified counter could not be found.
|
||||
pdhCstatusInvalidData = 0xC0000BBA // The counter was successfully found, but the data returned is not valid.
|
||||
pdhMemoryAllocationFailure = 0xC0000BBB
|
||||
pdhInvalidHandle = 0xC0000BBC
|
||||
pdhInvalidArgument = 0xC0000BBD // Required argument is missing or incorrect.
|
||||
pdhFunctionNotFound = 0xC0000BBE
|
||||
pdhCstatusNoCountername = 0xC0000BBF
|
||||
pdhCstatusBadCountername = 0xC0000BC0 // Unable to parse the counter path. Check the format and syntax of the specified path.
|
||||
pdhInvalidBuffer = 0xC0000BC1
|
||||
pdhInsufficientBuffer = 0xC0000BC2
|
||||
pdhCannotConnectMachine = 0xC0000BC3
|
||||
pdhInvalidPath = 0xC0000BC4
|
||||
pdhInvalidInstance = 0xC0000BC5
|
||||
pdhInvalidData = 0xC0000BC6 // specified counter does not contain valid data or a successful status code.
|
||||
pdhNoDialogData = 0xC0000BC7
|
||||
pdhCannotReadNameStrings = 0xC0000BC8
|
||||
pdhLogFileCreateError = 0xC0000BC9
|
||||
pdhLogFileOpenError = 0xC0000BCA
|
||||
pdhLogTypeNotFound = 0xC0000BCB
|
||||
pdhNoMoreData = 0xC0000BCC
|
||||
pdhEntryNotInLogFile = 0xC0000BCD
|
||||
pdhDataSourceIsLogFile = 0xC0000BCE
|
||||
pdhDataSourceIsRealTime = 0xC0000BCF
|
||||
pdhUnableReadLogHeader = 0xC0000BD0
|
||||
pdhFileNotFound = 0xC0000BD1
|
||||
pdhFileAlreadyExists = 0xC0000BD2
|
||||
pdhNotImplemented = 0xC0000BD3
|
||||
pdhStringNotFound = 0xC0000BD4
|
||||
pdhUnableMapNameFiles = 0x80000BD5
|
||||
pdhUnknownLogFormat = 0xC0000BD6
|
||||
pdhUnknownLogsvcCommand = 0xC0000BD7
|
||||
pdhLogsvcQueryNotFound = 0xC0000BD8
|
||||
pdhLogsvcNotOpened = 0xC0000BD9
|
||||
pdhWbemError = 0xC0000BDA
|
||||
pdhAccessDenied = 0xC0000BDB
|
||||
pdhLogFileTooSmall = 0xC0000BDC
|
||||
pdhInvalidDatasource = 0xC0000BDD
|
||||
pdhInvalidSqldb = 0xC0000BDE
|
||||
pdhNoCounters = 0xC0000BDF
|
||||
pdhSQLAllocFailed = 0xC0000BE0
|
||||
pdhSQLAllocconFailed = 0xC0000BE1
|
||||
pdhSQLExecDirectFailed = 0xC0000BE2
|
||||
pdhSQLFetchFailed = 0xC0000BE3
|
||||
pdhSQLRowcountFailed = 0xC0000BE4
|
||||
pdhSQLMoreResultsFailed = 0xC0000BE5
|
||||
pdhSQLConnectFailed = 0xC0000BE6
|
||||
pdhSQLBindFailed = 0xC0000BE7
|
||||
pdhCannotConnectWmiServer = 0xC0000BE8
|
||||
pdhPlaCollectionAlreadyRunning = 0xC0000BE9
|
||||
pdhPlaErrorScheduleOverlap = 0xC0000BEA
|
||||
pdhPlaCollectionNotFound = 0xC0000BEB
|
||||
pdhPlaErrorScheduleElapsed = 0xC0000BEC
|
||||
pdhPlaErrorNostart = 0xC0000BED
|
||||
pdhPlaErrorAlreadyExists = 0xC0000BEE
|
||||
pdhPlaErrorTypeMismatch = 0xC0000BEF
|
||||
pdhPlaErrorFilepath = 0xC0000BF0
|
||||
pdhPlaServiceError = 0xC0000BF1
|
||||
pdhPlaValidationError = 0xC0000BF2
|
||||
pdhPlaValidationWarning = 0x80000BF3
|
||||
pdhPlaErrorNameTooLong = 0xC0000BF4
|
||||
pdhInvalidSQLLogFormat = 0xC0000BF5
|
||||
pdhCounterAlreadyInQuery = 0xC0000BF6
|
||||
pdhBinaryLogCorrupt = 0xC0000BF7
|
||||
pdhLogSampleTooSmall = 0xC0000BF8
|
||||
pdhOsLaterVersion = 0xC0000BF9
|
||||
pdhOsEarlierVersion = 0xC0000BFA
|
||||
pdhIncorrectAppendTime = 0xC0000BFB
|
||||
pdhUnmatchedAppendCounter = 0xC0000BFC
|
||||
pdhSQLAlterDetailFailed = 0xC0000BFD
|
||||
pdhQueryPerfDataTimeout = 0xC0000BFE
|
||||
)
|
||||
|
||||
var pdhErrors = map[uint32]string{
|
||||
pdhCstatusValidData: "PDH_CSTATUS_VALID_DATA",
|
||||
pdhCstatusNewData: "PDH_CSTATUS_NEW_DATA",
|
||||
pdhCstatusNoMachine: "PDH_CSTATUS_NO_MACHINE",
|
||||
pdhCstatusNoInstance: "PDH_CSTATUS_NO_INSTANCE",
|
||||
pdhMoreData: "PDH_MORE_DATA",
|
||||
pdhCstatusItemNotValidated: "PDH_CSTATUS_ITEM_NOT_VALIDATED",
|
||||
pdhRetry: "PDH_RETRY",
|
||||
pdhNoData: "PDH_NO_DATA",
|
||||
pdhCalcNegativeDenominator: "PDH_CALC_NEGATIVE_DENOMINATOR",
|
||||
pdhCalcNegativeTimebase: "PDH_CALC_NEGATIVE_TIMEBASE",
|
||||
pdhCalcNegativeValue: "PDH_CALC_NEGATIVE_VALUE",
|
||||
pdhDialogCancelled: "PDH_DIALOG_CANCELLED",
|
||||
pdhEndOfLogFile: "PDH_END_OF_LOG_FILE",
|
||||
pdhAsyncQueryTimeout: "PDH_ASYNC_QUERY_TIMEOUT",
|
||||
pdhCannotSetDefaultRealtimeDatasource: "PDH_CANNOT_SET_DEFAULT_REALTIME_DATASOURCE",
|
||||
pdhCstatusNoObject: "PDH_CSTATUS_NO_OBJECT",
|
||||
pdhCstatusNoCounter: "PDH_CSTATUS_NO_COUNTER",
|
||||
pdhCstatusInvalidData: "PDH_CSTATUS_INVALID_DATA",
|
||||
pdhMemoryAllocationFailure: "PDH_MEMORY_ALLOCATION_FAILURE",
|
||||
pdhInvalidHandle: "PDH_INVALID_HANDLE",
|
||||
pdhInvalidArgument: "PDH_INVALID_ARGUMENT",
|
||||
pdhFunctionNotFound: "PDH_FUNCTION_NOT_FOUND",
|
||||
pdhCstatusNoCountername: "PDH_CSTATUS_NO_COUNTERNAME",
|
||||
pdhCstatusBadCountername: "PDH_CSTATUS_BAD_COUNTERNAME",
|
||||
pdhInvalidBuffer: "PDH_INVALID_BUFFER",
|
||||
pdhInsufficientBuffer: "PDH_INSUFFICIENT_BUFFER",
|
||||
pdhCannotConnectMachine: "PDH_CANNOT_CONNECT_MACHINE",
|
||||
pdhInvalidPath: "PDH_INVALID_PATH",
|
||||
pdhInvalidInstance: "PDH_INVALID_INSTANCE",
|
||||
pdhInvalidData: "PDH_INVALID_DATA",
|
||||
pdhNoDialogData: "PDH_NO_DIALOG_DATA",
|
||||
pdhCannotReadNameStrings: "PDH_CANNOT_READ_NAME_STRINGS",
|
||||
pdhLogFileCreateError: "PDH_LOG_FILE_CREATE_ERROR",
|
||||
pdhLogFileOpenError: "PDH_LOG_FILE_OPEN_ERROR",
|
||||
pdhLogTypeNotFound: "PDH_LOG_TYPE_NOT_FOUND",
|
||||
pdhNoMoreData: "PDH_NO_MORE_DATA",
|
||||
pdhEntryNotInLogFile: "PDH_ENTRY_NOT_IN_LOG_FILE",
|
||||
pdhDataSourceIsLogFile: "PDH_DATA_SOURCE_IS_LOG_FILE",
|
||||
pdhDataSourceIsRealTime: "PDH_DATA_SOURCE_IS_REAL_TIME",
|
||||
pdhUnableReadLogHeader: "PDH_UNABLE_READ_LOG_HEADER",
|
||||
pdhFileNotFound: "PDH_FILE_NOT_FOUND",
|
||||
pdhFileAlreadyExists: "PDH_FILE_ALREADY_EXISTS",
|
||||
pdhNotImplemented: "PDH_NOT_IMPLEMENTED",
|
||||
pdhStringNotFound: "PDH_STRING_NOT_FOUND",
|
||||
pdhUnableMapNameFiles: "PDH_UNABLE_MAP_NAME_FILES",
|
||||
pdhUnknownLogFormat: "PDH_UNKNOWN_LOG_FORMAT",
|
||||
pdhUnknownLogsvcCommand: "PDH_UNKNOWN_LOGSVC_COMMAND",
|
||||
pdhLogsvcQueryNotFound: "PDH_LOGSVC_QUERY_NOT_FOUND",
|
||||
pdhLogsvcNotOpened: "PDH_LOGSVC_NOT_OPENED",
|
||||
pdhWbemError: "PDH_WBEM_ERROR",
|
||||
pdhAccessDenied: "PDH_ACCESS_DENIED",
|
||||
pdhLogFileTooSmall: "PDH_LOG_FILE_TOO_SMALL",
|
||||
pdhInvalidDatasource: "PDH_INVALID_DATASOURCE",
|
||||
pdhInvalidSqldb: "PDH_INVALID_SQLDB",
|
||||
pdhNoCounters: "PDH_NO_COUNTERS",
|
||||
pdhSQLAllocFailed: "PDH_SQL_ALLOC_FAILED",
|
||||
pdhSQLAllocconFailed: "PDH_SQL_ALLOCCON_FAILED",
|
||||
pdhSQLExecDirectFailed: "PDH_SQL_EXEC_DIRECT_FAILED",
|
||||
pdhSQLFetchFailed: "PDH_SQL_FETCH_FAILED",
|
||||
pdhSQLRowcountFailed: "PDH_SQL_ROWCOUNT_FAILED",
|
||||
pdhSQLMoreResultsFailed: "PDH_SQL_MORE_RESULTS_FAILED",
|
||||
pdhSQLConnectFailed: "PDH_SQL_CONNECT_FAILED",
|
||||
pdhSQLBindFailed: "PDH_SQL_BIND_FAILED",
|
||||
pdhCannotConnectWmiServer: "PDH_CANNOT_CONNECT_WMI_SERVER",
|
||||
pdhPlaCollectionAlreadyRunning: "PDH_PLA_COLLECTION_ALREADY_RUNNING",
|
||||
pdhPlaErrorScheduleOverlap: "PDH_PLA_ERROR_SCHEDULE_OVERLAP",
|
||||
pdhPlaCollectionNotFound: "PDH_PLA_COLLECTION_NOT_FOUND",
|
||||
pdhPlaErrorScheduleElapsed: "PDH_PLA_ERROR_SCHEDULE_ELAPSED",
|
||||
pdhPlaErrorNostart: "PDH_PLA_ERROR_NOSTART",
|
||||
pdhPlaErrorAlreadyExists: "PDH_PLA_ERROR_ALREADY_EXISTS",
|
||||
pdhPlaErrorTypeMismatch: "PDH_PLA_ERROR_TYPE_MISMATCH",
|
||||
pdhPlaErrorFilepath: "PDH_PLA_ERROR_FILEPATH",
|
||||
pdhPlaServiceError: "PDH_PLA_SERVICE_ERROR",
|
||||
pdhPlaValidationError: "PDH_PLA_VALIDATION_ERROR",
|
||||
pdhPlaValidationWarning: "PDH_PLA_VALIDATION_WARNING",
|
||||
pdhPlaErrorNameTooLong: "PDH_PLA_ERROR_NAME_TOO_LONG",
|
||||
pdhInvalidSQLLogFormat: "PDH_INVALID_SQL_LOG_FORMAT",
|
||||
pdhCounterAlreadyInQuery: "PDH_COUNTER_ALREADY_IN_QUERY",
|
||||
pdhBinaryLogCorrupt: "PDH_BINARY_LOG_CORRUPT",
|
||||
pdhLogSampleTooSmall: "PDH_LOG_SAMPLE_TOO_SMALL",
|
||||
pdhOsLaterVersion: "PDH_OS_LATER_VERSION",
|
||||
pdhOsEarlierVersion: "PDH_OS_EARLIER_VERSION",
|
||||
pdhIncorrectAppendTime: "PDH_INCORRECT_APPEND_TIME",
|
||||
pdhUnmatchedAppendCounter: "PDH_UNMATCHED_APPEND_COUNTER",
|
||||
pdhSQLAlterDetailFailed: "PDH_SQL_ALTER_DETAIL_FAILED",
|
||||
pdhQueryPerfDataTimeout: "PDH_QUERY_PERF_DATA_TIMEOUT",
|
||||
}
|
||||
|
||||
// Formatting options for GetFormattedCounterValue().
|
||||
const (
|
||||
pdhFmtRaw = 0x00000010
|
||||
pdhFmtAnsi = 0x00000020
|
||||
pdhFmtUnicode = 0x00000040
|
||||
pdhFmtLong = 0x00000100 // Return data as a long int.
|
||||
pdhFmtDouble = 0x00000200 // Return data as a double precision floating point real.
|
||||
pdhFmtLarge = 0x00000400 // Return data as a 64 bit integer.
|
||||
pdhFmtNoscale = 0x00001000 // can be OR-ed: Do not apply the counter's default scaling factor.
|
||||
pdhFmt1000 = 0x00002000 // can be OR-ed: multiply the actual value by 1,000.
|
||||
pdhFmtNodata = 0x00004000 // can be OR-ed: unknown what this is for, MSDN says nothing.
|
||||
pdhFmtNocap100 = 0x00008000 // can be OR-ed: do not cap values > 100.
|
||||
perfDetailCostly = 0x00010000
|
||||
perfDetailStandard = 0x0000FFFF
|
||||
)
|
||||
|
||||
type (
|
||||
pdhQueryHandle handle // query handle
|
||||
pdhCounterHandle handle // counter handle
|
||||
)
|
||||
|
||||
var (
|
||||
// Library
|
||||
libPdhDll *syscall.DLL
|
||||
|
||||
// Functions
|
||||
pdhAddCounterWProc *syscall.Proc
|
||||
pdhAddEnglishCounterWProc *syscall.Proc
|
||||
pdhCloseQueryProc *syscall.Proc
|
||||
pdhCollectQueryDataProc *syscall.Proc
|
||||
pdhCollectQueryDataWithTimeProc *syscall.Proc
|
||||
pdhGetFormattedCounterValueProc *syscall.Proc
|
||||
pdhGetFormattedCounterArrayWProc *syscall.Proc
|
||||
pdhOpenQueryProc *syscall.Proc
|
||||
pdhExpandWildCardPathWProc *syscall.Proc
|
||||
pdhGetCounterInfoWProc *syscall.Proc
|
||||
pdhGetRawCounterValueProc *syscall.Proc
|
||||
pdhGetRawCounterArrayWProc *syscall.Proc
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Library
|
||||
libPdhDll = syscall.MustLoadDLL("pdh.dll")
|
||||
|
||||
// Functions
|
||||
pdhAddCounterWProc = libPdhDll.MustFindProc("PdhAddCounterW")
|
||||
pdhAddEnglishCounterWProc, _ = libPdhDll.FindProc("PdhAddEnglishCounterW") // XXX: only supported on versions > Vista.
|
||||
pdhCloseQueryProc = libPdhDll.MustFindProc("PdhCloseQuery")
|
||||
pdhCollectQueryDataProc = libPdhDll.MustFindProc("PdhCollectQueryData")
|
||||
pdhCollectQueryDataWithTimeProc, _ = libPdhDll.FindProc("PdhCollectQueryDataWithTime")
|
||||
pdhGetFormattedCounterValueProc = libPdhDll.MustFindProc("PdhGetFormattedCounterValue")
|
||||
pdhGetFormattedCounterArrayWProc = libPdhDll.MustFindProc("PdhGetFormattedCounterArrayW")
|
||||
pdhOpenQueryProc = libPdhDll.MustFindProc("PdhOpenQuery")
|
||||
pdhExpandWildCardPathWProc = libPdhDll.MustFindProc("PdhExpandWildCardPathW")
|
||||
pdhGetCounterInfoWProc = libPdhDll.MustFindProc("PdhGetCounterInfoW")
|
||||
pdhGetRawCounterValueProc = libPdhDll.MustFindProc("PdhGetRawCounterValue")
|
||||
pdhGetRawCounterArrayWProc = libPdhDll.MustFindProc("PdhGetRawCounterArrayW")
|
||||
}
|
||||
|
||||
// pdhAddCounter adds the specified counter to the query. This is the internationalized version. Preferably, use the
|
||||
// function pdhAddEnglishCounter instead. hQuery is the query handle, which has been fetched by pdhOpenQuery.
|
||||
// szFullCounterPath is a full, internationalized counter path (this will differ per Windows language version).
|
||||
// dwUserData is a 'user-defined value', which becomes part of the counter information. To retrieve this value
|
||||
// later, call pdhGetCounterInfo() and access dwQueryUserData of the pdhCounterInfo structure.
|
||||
//
|
||||
// Examples of szFullCounterPath (in an English version of Windows):
|
||||
//
|
||||
// \\Processor(_Total)\\% Idle Time
|
||||
// \\Processor(_Total)\\% Processor Time
|
||||
// \\LogicalDisk(C:)\% Free Space
|
||||
//
|
||||
// To view all (internationalized...) counters on a system, there are three non-programmatic ways: perfmon utility,
|
||||
// the typeperf command, and the registry editor. perfmon.exe is perhaps the easiest way, because it's basically a
|
||||
// full implementation of the pdh.dll API, except with a GUI and all that. The registry setting also provides an
|
||||
// interface to the available counters, and can be found at the following key:
|
||||
//
|
||||
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\CurrentLanguage
|
||||
//
|
||||
// This registry key contains several values as follows:
|
||||
//
|
||||
// 1
|
||||
// 1847
|
||||
// 2
|
||||
// System
|
||||
// 4
|
||||
// Memory
|
||||
// 6
|
||||
// % Processor Time
|
||||
// ... many, many more
|
||||
//
|
||||
// Somehow, these numeric values can be used as szFullCounterPath too:
|
||||
//
|
||||
// \2\6 will correspond to \\System\% Processor Time
|
||||
//
|
||||
// The typeperf command may also be pretty easy. To find all performance counters, simply execute:
|
||||
//
|
||||
// typeperf -qx
|
||||
func pdhAddCounter(hQuery pdhQueryHandle, szFullCounterPath string, dwUserData uintptr, phCounter *pdhCounterHandle) uint32 {
|
||||
ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath)
|
||||
ret, _, _ := pdhAddCounterWProc.Call(
|
||||
uintptr(hQuery),
|
||||
uintptr(unsafe.Pointer(ptxt)), //nolint:gosec // G103: Valid use of unsafe call to pass ptxt
|
||||
dwUserData,
|
||||
uintptr(unsafe.Pointer(phCounter))) //nolint:gosec // G103: Valid use of unsafe call to pass phCounter
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// pdhAddEnglishCounterSupported returns true if PdhAddEnglishCounterW Win API function was found in pdh.dll.
|
||||
// PdhAddEnglishCounterW function is not supported on pre-Windows Vista systems
|
||||
func pdhAddEnglishCounterSupported() bool {
|
||||
return pdhAddEnglishCounterWProc != nil
|
||||
}
|
||||
|
||||
// pdhAddEnglishCounter adds the specified language-neutral counter to the query. See the pdhAddCounter function. This function only exists on
|
||||
// Windows versions higher than Vista.
|
||||
func pdhAddEnglishCounter(hQuery pdhQueryHandle, szFullCounterPath string, dwUserData uintptr, phCounter *pdhCounterHandle) uint32 {
|
||||
if pdhAddEnglishCounterWProc == nil {
|
||||
return errorInvalidFunction
|
||||
}
|
||||
|
||||
ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath)
|
||||
ret, _, _ := pdhAddEnglishCounterWProc.Call(
|
||||
uintptr(hQuery),
|
||||
uintptr(unsafe.Pointer(ptxt)), //nolint:gosec // G103: Valid use of unsafe call to pass ptxt
|
||||
dwUserData,
|
||||
uintptr(unsafe.Pointer(phCounter))) //nolint:gosec // G103: Valid use of unsafe call to pass phCounter
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// pdhCloseQuery closes all counters contained in the specified query, closes all handles related to the query,
|
||||
// and frees all memory associated with the query.
|
||||
func pdhCloseQuery(hQuery pdhQueryHandle) uint32 {
|
||||
ret, _, _ := pdhCloseQueryProc.Call(uintptr(hQuery))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// pdhCollectQueryData collects the current raw data value for all counters in the specified query and updates the status
|
||||
// code of each counter. With some counters, this function needs to be repeatedly called before the value
|
||||
// of the counter can be extracted with PdhGetFormattedCounterValue(). For example, the following code
|
||||
// requires at least two calls:
|
||||
//
|
||||
// var handle win.PDH_HQUERY
|
||||
// var counterHandle win.PDH_HCOUNTER
|
||||
// ret := win.pdhOpenQuery(0, 0, &handle)
|
||||
// ret = win.pdhAddEnglishCounter(handle, "\\Processor(_Total)\\% Idle Time", 0, &counterHandle)
|
||||
// var derp win.PDH_FMT_COUNTERVALUE_DOUBLE
|
||||
//
|
||||
// ret = win.pdhCollectQueryData(handle)
|
||||
// fmt.Printf("Collect return code is %x\n", ret) // return code will be PDH_CSTATUS_INVALID_DATA
|
||||
// ret = win.pdhGetFormattedCounterValueDouble(counterHandle, 0, &derp)
|
||||
//
|
||||
// ret = win.pdhCollectQueryData(handle)
|
||||
// fmt.Printf("Collect return code is %x\n", ret) // return code will be ERROR_SUCCESS
|
||||
// ret = win.pdhGetFormattedCounterValueDouble(counterHandle, 0, &derp)
|
||||
//
|
||||
// The pdhCollectQueryData will return an error in the first call because it needs two values for
|
||||
// displaying the correct data for the processor idle time. The second call will have a 0 return code.
|
||||
func pdhCollectQueryData(hQuery pdhQueryHandle) uint32 {
|
||||
ret, _, _ := pdhCollectQueryDataProc.Call(uintptr(hQuery))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// pdhCollectQueryDataWithTime queries data from perfmon, retrieving the device/windows timestamp from the node it was collected on.
|
||||
// Converts the filetime structure to a GO time class and returns the native time.
|
||||
func pdhCollectQueryDataWithTime(hQuery pdhQueryHandle) (uint32, time.Time) {
|
||||
var localFileTime fileTime
|
||||
//nolint:gosec // G103: Valid use of unsafe call to pass localFileTime
|
||||
ret, _, _ := pdhCollectQueryDataWithTimeProc.Call(uintptr(hQuery), uintptr(unsafe.Pointer(&localFileTime)))
|
||||
|
||||
if ret == errorSuccess {
|
||||
var utcFileTime fileTime
|
||||
ret, _, _ := kernelLocalFileTimeToFileTime.Call(
|
||||
uintptr(unsafe.Pointer(&localFileTime)), //nolint:gosec // G103: Valid use of unsafe call to pass localFileTime
|
||||
uintptr(unsafe.Pointer(&utcFileTime))) //nolint:gosec // G103: Valid use of unsafe call to pass utcFileTime
|
||||
|
||||
if ret == 0 {
|
||||
return uint32(errorFailure), time.Now()
|
||||
}
|
||||
|
||||
// First convert 100-ns intervals to microseconds, then adjust for the
|
||||
// epoch difference
|
||||
var totalMicroSeconds int64
|
||||
totalMicroSeconds = ((int64(utcFileTime.dwHighDateTime) << 32) | int64(utcFileTime.dwLowDateTime)) / 10
|
||||
totalMicroSeconds -= epochDifferenceMicros
|
||||
|
||||
retTime := time.Unix(0, totalMicroSeconds*1000)
|
||||
|
||||
return uint32(errorSuccess), retTime
|
||||
}
|
||||
|
||||
return uint32(ret), time.Now()
|
||||
}
|
||||
|
||||
// pdhGetFormattedCounterValueDouble formats the given hCounter using a 'double'. The result is set into the specialized union struct pValue.
|
||||
// This function does not directly translate to a Windows counterpart due to union specialization tricks.
|
||||
func pdhGetFormattedCounterValueDouble(hCounter pdhCounterHandle, lpdwType *uint32, pValue *pdhFmtCountervalueDouble) uint32 {
|
||||
ret, _, _ := pdhGetFormattedCounterValueProc.Call(
|
||||
uintptr(hCounter),
|
||||
uintptr(pdhFmtDouble|pdhFmtNocap100),
|
||||
uintptr(unsafe.Pointer(lpdwType)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwType
|
||||
uintptr(unsafe.Pointer(pValue))) //nolint:gosec // G103: Valid use of unsafe call to pass pValue
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// pdhGetFormattedCounterArrayDouble returns an array of formatted counter values. Use this function when you want to format the counter values of a
|
||||
// counter that contains a wildcard character for the instance name. The itemBuffer must a slice of type pdhFmtCountervalueItemDouble.
|
||||
// An example of how this function can be used:
|
||||
//
|
||||
// okPath := "\\Process(*)\\% Processor Time" // notice the wildcard * character
|
||||
//
|
||||
// // omitted all necessary stuff ...
|
||||
//
|
||||
// var bufSize uint32
|
||||
// var bufCount uint32
|
||||
// var size uint32 = uint32(unsafe.Sizeof(win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE{}))
|
||||
// var emptyBuf [1]win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE // need at least 1 addressable null ptr.
|
||||
//
|
||||
// for {
|
||||
// // collect
|
||||
// ret := win.pdhCollectQueryData(queryHandle)
|
||||
// if ret == win.ERROR_SUCCESS {
|
||||
// ret = win.pdhGetFormattedCounterArrayDouble(counterHandle, &bufSize, &bufCount, &emptyBuf[0]) // uses null ptr here according to MSDN.
|
||||
// if ret == win.PDH_MORE_DATA {
|
||||
// filledBuf := make([]win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size)
|
||||
// ret = win.pdhGetFormattedCounterArrayDouble(counterHandle, &bufSize, &bufCount, &filledBuf[0])
|
||||
// for i := 0; i < int(bufCount); i++ {
|
||||
// c := filledBuf[i]
|
||||
// var s string = win.utf16PtrToString(c.SzName)
|
||||
// fmt.Printf("Index %d -> %s, value %v\n", i, s, c.FmtValue.DoubleValue)
|
||||
// }
|
||||
//
|
||||
// filledBuf = nil
|
||||
// // Need to at least set bufSize to zero, because if not, the function will not
|
||||
// // return PDH_MORE_DATA and will not set the bufSize.
|
||||
// bufCount = 0
|
||||
// bufSize = 0
|
||||
// }
|
||||
//
|
||||
// time.Sleep(2000 * time.Millisecond)
|
||||
// }
|
||||
// }
|
||||
func pdhGetFormattedCounterArrayDouble(hCounter pdhCounterHandle, lpdwBufferSize, lpdwBufferCount *uint32, itemBuffer *byte) uint32 {
|
||||
ret, _, _ := pdhGetFormattedCounterArrayWProc.Call(
|
||||
uintptr(hCounter),
|
||||
uintptr(pdhFmtDouble|pdhFmtNocap100),
|
||||
uintptr(unsafe.Pointer(lpdwBufferSize)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwBufferSize
|
||||
uintptr(unsafe.Pointer(lpdwBufferCount)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwBufferCount
|
||||
uintptr(unsafe.Pointer(itemBuffer))) //nolint:gosec // G103: Valid use of unsafe call to pass itemBuffer
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// pdhOpenQuery creates a new query that is used to manage the collection of performance data.
|
||||
// szDataSource is a null terminated string that specifies the name of the log file from which to
|
||||
// retrieve the performance data. If 0, performance data is collected from a real-time data source.
|
||||
// dwUserData is a user-defined value to associate with this query. To retrieve the user data later,
|
||||
// call pdhGetCounterInfo and access dwQueryUserData of the pdhCounterInfo structure. phQuery is
|
||||
// the handle to the query, and must be used in subsequent calls. This function returns a PDH_
|
||||
// constant error code, or errorSuccess if the call succeeded.
|
||||
func pdhOpenQuery(szDataSource, dwUserData uintptr, phQuery *pdhQueryHandle) uint32 {
|
||||
ret, _, _ := pdhOpenQueryProc.Call(
|
||||
szDataSource,
|
||||
dwUserData,
|
||||
uintptr(unsafe.Pointer(phQuery))) //nolint:gosec // G103: Valid use of unsafe call to pass phQuery
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// pdhExpandWildCardPath examines the specified computer or log file and returns those counter paths that match the given counter path
|
||||
// which contains wildcard characters. The general counter path format is as follows:
|
||||
//
|
||||
// \\computer\object(parent/instance#index)\counter
|
||||
//
|
||||
// The parent, instance, index, and counter components of the counter path may contain either a valid name or a wildcard character.
|
||||
// The computer, parent, instance, and index components are not necessary for all counters.
|
||||
//
|
||||
// The following is a list of the possible formats:
|
||||
//
|
||||
// \\computer\object(parent/instance#index)\counter
|
||||
// \\computer\object(parent/instance)\counter
|
||||
// \\computer\object(instance#index)\counter
|
||||
// \\computer\object(instance)\counter
|
||||
// \\computer\object\counter
|
||||
// \object(parent/instance#index)\counter
|
||||
// \object(parent/instance)\counter
|
||||
// \object(instance#index)\counter
|
||||
// \object(instance)\counter
|
||||
// \object\counter
|
||||
// Use an asterisk (*) as the wildcard character, for example, \object(*)\counter.
|
||||
//
|
||||
// If a wildcard character is specified in the parent name, all instances of the specified object
|
||||
// that match the specified instance and counter fields will be returned.
|
||||
// For example, \object(*/instance)\counter.
|
||||
//
|
||||
// If a wildcard character is specified in the instance name, all instances of the specified object and parent object will be returned if all instance names
|
||||
// corresponding to the specified index match the wildcard character. For example, \object(parent/*)\counter.
|
||||
// If the object does not contain an instance, an error occurs.
|
||||
//
|
||||
// If a wildcard character is specified in the counter name, all counters of the specified object are returned.
|
||||
//
|
||||
// Partial counter path string matches (for example, "pro*") are supported.
|
||||
func pdhExpandWildCardPath(szWildCardPath string, mszExpandedPathList *uint16, pcchPathListLength *uint32) uint32 {
|
||||
ptxt, _ := syscall.UTF16PtrFromString(szWildCardPath)
|
||||
flags := uint32(0) // expand instances and counters
|
||||
ret, _, _ := pdhExpandWildCardPathWProc.Call(
|
||||
0, // search counters on local computer
|
||||
uintptr(unsafe.Pointer(ptxt)), //nolint:gosec // G103: Valid use of unsafe call to pass ptxt
|
||||
uintptr(unsafe.Pointer(mszExpandedPathList)), //nolint:gosec // G103: Valid use of unsafe call to pass mszExpandedPathList
|
||||
uintptr(unsafe.Pointer(pcchPathListLength)), //nolint:gosec // G103: Valid use of unsafe call to pass pcchPathListLength
|
||||
uintptr(unsafe.Pointer(&flags))) //nolint:gosec // G103: Valid use of unsafe call to pass flags
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
func pdhFormatError(msgID uint32) string {
|
||||
var flags uint32 = windows.FORMAT_MESSAGE_FROM_HMODULE | windows.FORMAT_MESSAGE_ARGUMENT_ARRAY | windows.FORMAT_MESSAGE_IGNORE_INSERTS
|
||||
buf := make([]uint16, 300)
|
||||
_, err := windows.FormatMessage(flags, uintptr(libPdhDll.Handle), msgID, 0, buf, nil)
|
||||
if err == nil {
|
||||
return utf16PtrToString(&buf[0])
|
||||
}
|
||||
return fmt.Sprintf("(pdhErr=%d) %s", msgID, err.Error())
|
||||
}
|
||||
|
||||
// pdhGetCounterInfo retrieves information about a counter, such as data size, counter type, path, and user-supplied data values
|
||||
// hCounter [in]
|
||||
// Handle of the counter from which you want to retrieve information. The pdhAddCounter function returns this handle.
|
||||
//
|
||||
// bRetrieveExplainText [in]
|
||||
// Determines whether explain text is retrieved. If you set this parameter to TRUE, the explain text for the counter is retrieved.
|
||||
// If you set this parameter to FALSE, the field in the returned buffer is NULL.
|
||||
//
|
||||
// pdwBufferSize [in, out]
|
||||
// Size of the lpBuffer buffer, in bytes. If zero on input, the function returns PdhMoreData and sets this parameter to the required buffer size.
|
||||
// If the buffer is larger than the required size, the function sets this parameter to the actual size of the buffer that was used.
|
||||
// If the specified size on input is greater than zero but less than the required size, you should not rely on the returned size to reallocate the buffer.
|
||||
//
|
||||
// lpBuffer [out]
|
||||
// Caller-allocated buffer that receives a pdhCounterInfo structure.
|
||||
// The structure is variable-length, because the string data is appended to the end of the fixed-format portion of the structure.
|
||||
// This is done so that all data is returned in a single buffer allocated by the caller. Set to NULL if pdwBufferSize is zero.
|
||||
func pdhGetCounterInfo(hCounter pdhCounterHandle, bRetrieveExplainText int, pdwBufferSize *uint32, lpBuffer *byte) uint32 {
|
||||
ret, _, _ := pdhGetCounterInfoWProc.Call(
|
||||
uintptr(hCounter),
|
||||
uintptr(bRetrieveExplainText),
|
||||
uintptr(unsafe.Pointer(pdwBufferSize)), //nolint:gosec // G103: Valid use of unsafe call to pass pdwBufferSize
|
||||
uintptr(unsafe.Pointer(lpBuffer))) //nolint:gosec // G103: Valid use of unsafe call to pass lpBuffer
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// pdhGetRawCounterValue returns the current raw value of the counter.
|
||||
// If the specified counter instance does not exist, this function will return errorSuccess
|
||||
// and the CStatus member of the pdhRawCounter structure will contain PdhCstatusNoInstance.
|
||||
//
|
||||
// hCounter [in]
|
||||
// Handle of the counter from which to retrieve the current raw value. The pdhAddCounter function returns this handle.
|
||||
//
|
||||
// lpdwType [out]
|
||||
// Receives the counter type. For a list of counter types, see the Counter Types section of the Windows Server 2003 Deployment Kit.
|
||||
// This parameter is optional.
|
||||
//
|
||||
// pValue [out]
|
||||
// A pdhRawCounter structure that receives the counter value.
|
||||
func pdhGetRawCounterValue(hCounter pdhCounterHandle, lpdwType *uint32, pValue *pdhRawCounter) uint32 {
|
||||
ret, _, _ := pdhGetRawCounterValueProc.Call(
|
||||
uintptr(hCounter),
|
||||
uintptr(unsafe.Pointer(lpdwType)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwType
|
||||
uintptr(unsafe.Pointer(pValue))) //nolint:gosec // G103: Valid use of unsafe call to pass pValue
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// pdhGetRawCounterArray returns an array of raw values from the specified counter. Use this function when you want to retrieve the raw counter values
|
||||
// of a counter that contains a wildcard character for the instance name.
|
||||
// hCounter
|
||||
// Handle of the counter for whose current raw instance values you want to retrieve. The pdhAddCounter function returns this handle.
|
||||
//
|
||||
// lpdwBufferSize
|
||||
// Size of the ItemBuffer buffer, in bytes. If zero on input, the function returns PdhMoreData and sets this parameter to the required buffer size.
|
||||
// If the buffer is larger than the required size, the function sets this parameter to the actual size of the buffer that was used.
|
||||
// If the specified size on input is greater than zero but less than the required size, you should not rely on the returned size to reallocate the buffer.
|
||||
//
|
||||
// lpdwItemCount
|
||||
// Number of raw counter values in the ItemBuffer buffer.
|
||||
//
|
||||
// ItemBuffer
|
||||
// Caller-allocated buffer that receives the array of pdhRawCounterItem structures; the structures contain the raw instance counter values.
|
||||
// Set to NULL if lpdwBufferSize is zero.
|
||||
func pdhGetRawCounterArray(hCounter pdhCounterHandle, lpdwBufferSize, lpdwBufferCount *uint32, itemBuffer *byte) uint32 {
|
||||
ret, _, _ := pdhGetRawCounterArrayWProc.Call(
|
||||
uintptr(hCounter),
|
||||
uintptr(unsafe.Pointer(lpdwBufferSize)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwBufferSize
|
||||
uintptr(unsafe.Pointer(lpdwBufferCount)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwBufferCount
|
||||
uintptr(unsafe.Pointer(itemBuffer))) //nolint:gosec // G103: Valid use of unsafe call to pass itemBuffer
|
||||
return uint32(ret)
|
||||
}
|
126
plugins/inputs/win_perf_counters/pdh_386.go
Normal file
126
plugins/inputs/win_perf_counters/pdh_386.go
Normal file
|
@ -0,0 +1,126 @@
|
|||
// Copyright (c) 2010 The win Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This is the official list of 'win' authors for copyright purposes.
|
||||
//
|
||||
// Alexander Neumann <an2048@googlemail.com>
|
||||
// Joseph Watson <jtwatson@linux-consulting.us>
|
||||
// Kevin Pors <krpors@gmail.com>
|
||||
|
||||
//go:build windows
|
||||
|
||||
package win_perf_counters
|
||||
|
||||
// pdhFmtCountervalueDouble is a union specialization for double values
|
||||
type pdhFmtCountervalueDouble struct {
|
||||
CStatus uint32
|
||||
padding [4]byte
|
||||
DoubleValue float64
|
||||
}
|
||||
|
||||
// pdhFmtCountervalueLong is a union specialization for long values
|
||||
type pdhFmtCountervalueLong struct {
|
||||
CStatus uint32
|
||||
LongValue int32
|
||||
padding [4]byte //nolint:unused // Memory reservation
|
||||
}
|
||||
|
||||
type pdhFmtCountervalueItemDouble struct {
|
||||
SzName *uint16
|
||||
padding [4]byte //nolint:unused // Memory reservation
|
||||
FmtValue pdhFmtCountervalueDouble
|
||||
}
|
||||
|
||||
// pdhCounterInfo structure contains information describing the properties of a counter. This information also includes the counter path.
|
||||
type pdhCounterInfo struct {
|
||||
//Size of the structure, including the appended strings, in bytes.
|
||||
DwLength uint32
|
||||
//Counter type. For a list of counter types, see the Counter Types section of the
|
||||
//<a "href=http://go.microsoft.com/fwlink/p/?linkid=84422">Windows Server 2003 Deployment Kit</a>.
|
||||
//The counter type constants are defined in Winperf.h.
|
||||
DwType uint32
|
||||
//Counter version information. Not used.
|
||||
CVersion uint32
|
||||
//Counter status that indicates if the counter value is valid. For a list of possible values,
|
||||
//see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa371894(v=vs.85).aspx">Checking PDH Interface Return Values</a>.
|
||||
CStatus uint32
|
||||
//Scale factor to use when computing the displayable value of the counter. The scale factor is a power of ten.
|
||||
//The valid range of this parameter is PDH_MIN_SCALE (–7) (the returned value is the actual value times 10–⁷) to
|
||||
//PDH_MAX_SCALE (+7) (the returned value is the actual value times 10⁺⁷). A value of zero will set the scale to one, so that the actual value is returned
|
||||
LScale int32
|
||||
//Default scale factor as suggested by the counter's provider.
|
||||
LDefaultScale int32
|
||||
//The value passed in the dwUserData parameter when calling pdhAddCounter.
|
||||
DwUserData *uint32
|
||||
//The value passed in the dwUserData parameter when calling pdhOpenQuery.
|
||||
DwQueryUserData *uint32
|
||||
//Null-terminated string that specifies the full counter path. The string follows this structure in memory.
|
||||
SzFullPath *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the computer specified in the counter path. Is NULL, if the path does not specify a computer.
|
||||
//The string follows this structure in memory.
|
||||
SzMachineName *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the performance object specified in the counter path. The string follows this structure in memory.
|
||||
SzObjectName *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the object instance specified in the counter path. Is NULL, if the path does not specify an instance.
|
||||
//The string follows this structure in memory.
|
||||
SzInstanceName *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the parent instance specified in the counter path. Is NULL,
|
||||
//if the path does not specify a parent instance.
|
||||
//The string follows this structure in memory.
|
||||
SzParentInstance *uint16 // pointer to a string
|
||||
//Instance index specified in the counter path. Is 0, if the path does not specify an instance index.
|
||||
DwInstanceIndex uint32 // pointer to a string
|
||||
//Null-terminated string that contains the counter name. The string follows this structure in memory.
|
||||
SzCounterName *uint16 // pointer to a string
|
||||
//padding
|
||||
Padding [4]byte
|
||||
//Help text that describes the counter. Is NULL if the source is a log file.
|
||||
SzExplainText *uint16 // pointer to a string
|
||||
//Start of the string data that is appended to the structure.
|
||||
DataBuffer [1]uint32 // pointer to an extra space
|
||||
}
|
||||
|
||||
// The pdhRawCounter structure returns the data as it was collected from the counter provider. No translation, formatting,
|
||||
// or other interpretation is performed on the data
|
||||
type pdhRawCounter struct {
|
||||
// Counter status that indicates if the counter value is valid. Check this member before using the data in a calculation or displaying its value.
|
||||
// For a list of possible values, see https://docs.microsoft.com/windows/desktop/PerfCtrs/checking-pdh-interface-return-values
|
||||
CStatus uint32
|
||||
// Local time for when the data was collected
|
||||
TimeStamp fileTime
|
||||
// First raw counter value.
|
||||
FirstValue int64
|
||||
// Second raw counter value. Rate counters require two values in order to compute a displayable value.
|
||||
SecondValue int64
|
||||
// If the counter type contains the PERF_MULTI_COUNTER flag, this member contains the additional counter data used in the calculation.
|
||||
// For example, the PERF_100NSEC_MULTI_TIMER counter type contains the PERF_MULTI_COUNTER flag.
|
||||
MultiCount uint32
|
||||
}
|
||||
|
||||
type pdhRawCounterItem struct {
|
||||
// Pointer to a null-terminated string that specifies the instance name of the counter. The string is appended to the end of this structure.
|
||||
SzName *uint16
|
||||
//A pdhRawCounter structure that contains the raw counter value of the instance
|
||||
RawValue pdhRawCounter
|
||||
}
|
115
plugins/inputs/win_perf_counters/pdh_amd64.go
Normal file
115
plugins/inputs/win_perf_counters/pdh_amd64.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
// Copyright (c) 2010 The win Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This is the official list of 'win' authors for copyright purposes.
|
||||
//
|
||||
// Alexander Neumann <an2048@googlemail.com>
|
||||
// Joseph Watson <jtwatson@linux-consulting.us>
|
||||
// Kevin Pors <krpors@gmail.com>
|
||||
|
||||
//go:build windows
|
||||
|
||||
package win_perf_counters
|
||||
|
||||
// pdhFmtCountervalueDouble is a union specialization for double values
|
||||
type pdhFmtCountervalueDouble struct {
|
||||
CStatus uint32
|
||||
DoubleValue float64
|
||||
}
|
||||
|
||||
// pdhFmtCountervalueItemDouble is a union specialization for double values, used by pdhGetFormattedCounterArrayDouble
|
||||
type pdhFmtCountervalueItemDouble struct {
|
||||
SzName *uint16
|
||||
FmtValue pdhFmtCountervalueDouble
|
||||
}
|
||||
|
||||
// pdhCounterInfo structure contains information describing the properties of a counter. This information also includes the counter path.
|
||||
type pdhCounterInfo struct {
|
||||
// Size of the structure, including the appended strings, in bytes.
|
||||
DwLength uint32
|
||||
// Counter type. For a list of counter types,
|
||||
// see the Counter Types section of the <a "href=http://go.microsoft.com/fwlink/p/?linkid=84422">Windows Server 2003 Deployment Kit</a>.
|
||||
// The counter type constants are defined in Winperf.h.
|
||||
DwType uint32
|
||||
// Counter version information. Not used.
|
||||
CVersion uint32
|
||||
// Counter status that indicates if the counter value is valid. For a list of possible values,
|
||||
// see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa371894(v=vs.85).aspx">Checking PDH Interface Return Values</a>.
|
||||
CStatus uint32
|
||||
// Scale factor to use when computing the displayable value of the counter. The scale factor is a power of ten.
|
||||
// The valid range of this parameter is PDH_MIN_SCALE (–7) (the returned value is the actual value times 10–⁷) to
|
||||
// PDH_MAX_SCALE (+7) (the returned value is the actual value times 10⁺⁷). A value of zero will set the scale to one, so that the actual value is returned
|
||||
LScale int32
|
||||
// Default scale factor as suggested by the counter's provider.
|
||||
LDefaultScale int32
|
||||
// The value passed in the dwUserData parameter when calling pdhAddCounter.
|
||||
DwUserData *uint32
|
||||
// The value passed in the dwUserData parameter when calling pdhOpenQuery.
|
||||
DwQueryUserData *uint32
|
||||
// Null-terminated string that specifies the full counter path. The string follows this structure in memory.
|
||||
SzFullPath *uint16 // pointer to a string
|
||||
// Null-terminated string that contains the name of the computer specified in the counter path. Is NULL, if the path does not specify a computer.
|
||||
// The string follows this structure in memory.
|
||||
SzMachineName *uint16 // pointer to a string
|
||||
// Null-terminated string that contains the name of the performance object specified in the counter path. The string follows this structure in memory.
|
||||
SzObjectName *uint16 // pointer to a string
|
||||
// Null-terminated string that contains the name of the object instance specified in the counter path. Is NULL, if the path does not specify an instance.
|
||||
// The string follows this structure in memory.
|
||||
SzInstanceName *uint16 // pointer to a string
|
||||
// Null-terminated string that contains the name of the parent instance specified in the counter path.
|
||||
// Is NULL, if the path does not specify a parent instance. The string follows this structure in memory.
|
||||
SzParentInstance *uint16 // pointer to a string
|
||||
// Instance index specified in the counter path. Is 0, if the path does not specify an instance index.
|
||||
DwInstanceIndex uint32 // pointer to a string
|
||||
// Null-terminated string that contains the counter name. The string follows this structure in memory.
|
||||
SzCounterName *uint16 // pointer to a string
|
||||
// Help text that describes the counter. Is NULL if the source is a log file.
|
||||
SzExplainText *uint16 // pointer to a string
|
||||
// Start of the string data that is appended to the structure.
|
||||
DataBuffer [1]uint32 // pointer to an extra space
|
||||
}
|
||||
|
||||
// The pdhRawCounter structure returns the data as it was collected from the counter provider.
|
||||
// No translation, formatting, or other interpretation is performed on the data
|
||||
type pdhRawCounter struct {
|
||||
// Counter status that indicates if the counter value is valid. Check this member before using the data in a calculation or displaying its value.
|
||||
// For a list of possible values, see https://docs.microsoft.com/windows/desktop/PerfCtrs/checking-pdh-interface-return-values
|
||||
CStatus uint32
|
||||
// Local time for when the data was collected
|
||||
TimeStamp fileTime
|
||||
// First raw counter value.
|
||||
FirstValue int64
|
||||
// Second raw counter value. Rate counters require two values in order to compute a displayable value.
|
||||
SecondValue int64
|
||||
// If the counter type contains the PERF_MULTI_COUNTER flag, this member contains the additional counter data used in the calculation.
|
||||
// For example, the PERF_100NSEC_MULTI_TIMER counter type contains the PERF_MULTI_COUNTER flag.
|
||||
MultiCount uint32
|
||||
}
|
||||
|
||||
type pdhRawCounterItem struct {
|
||||
// Pointer to a null-terminated string that specifies the instance name of the counter. The string is appended to the end of this structure.
|
||||
SzName *uint16
|
||||
// A pdhRawCounter structure that contains the raw counter value of the instance
|
||||
RawValue pdhRawCounter
|
||||
}
|
115
plugins/inputs/win_perf_counters/pdh_arm64.go
Normal file
115
plugins/inputs/win_perf_counters/pdh_arm64.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
// Copyright (c) 2010 The win Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This is the official list of 'win' authors for copyright purposes.
|
||||
//
|
||||
// Alexander Neumann <an2048@googlemail.com>
|
||||
// Joseph Watson <jtwatson@linux-consulting.us>
|
||||
// Kevin Pors <krpors@gmail.com>
|
||||
|
||||
//go:build windows
|
||||
|
||||
package win_perf_counters
|
||||
|
||||
// pdhFmtCountervalueDouble is a union specialization for double values
|
||||
type pdhFmtCountervalueDouble struct {
|
||||
CStatus uint32
|
||||
DoubleValue float64
|
||||
}
|
||||
|
||||
type pdhFmtCountervalueItemDouble struct {
|
||||
SzName *uint16
|
||||
FmtValue pdhFmtCountervalueDouble
|
||||
}
|
||||
|
||||
// pdhCounterInfo structure contains information describing the properties of a counter. This information also includes the counter path.
|
||||
type pdhCounterInfo struct {
|
||||
//Size of the structure, including the appended strings, in bytes.
|
||||
DwLength uint32
|
||||
//Counter type. For a list of counter types, see the Counter Types section
|
||||
//of the Windows Server 2003 Deployment Kit (http://go.microsoft.com/fwlink/p/?linkid=84422).
|
||||
//The counter type constants are defined in Winperf.h.
|
||||
DwType uint32
|
||||
//Counter version information. Not used.
|
||||
CVersion uint32
|
||||
//Counter status that indicates if the counter value is valid. For a list of possible values,
|
||||
//see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa371894(v=vs.85).aspx">Checking PDH Interface Return Values</a>.
|
||||
CStatus uint32
|
||||
//Scale factor to use when computing the displayable value of the counter. The scale factor is a power of ten.
|
||||
//The valid range of this parameter is PDH_MIN_SCALE (–7) (the returned value is the actual value times 10–⁷) to
|
||||
//PDH_MAX_SCALE (+7) (the returned value is the actual value times 10⁺⁷). A value of zero will set the scale to one, so that the actual value is returned
|
||||
LScale int32
|
||||
//Default scale factor as suggested by the counter's provider.
|
||||
LDefaultScale int32
|
||||
//The value passed in the dwUserData parameter when calling pdhAddCounter.
|
||||
DwUserData *uint32
|
||||
//The value passed in the dwUserData parameter when calling pdhOpenQuery.
|
||||
DwQueryUserData *uint32
|
||||
//Null-terminated string that specifies the full counter path. The string follows this structure in memory.
|
||||
SzFullPath *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the computer specified in the counter path. Is NULL, if the path does not specify a computer.
|
||||
//The string follows this structure in memory.
|
||||
SzMachineName *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the performance object specified in the counter path. The string follows this structure in memory.
|
||||
SzObjectName *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the object instance specified in the counter path. Is NULL, if the path does not specify an instance.
|
||||
//The string follows this structure in memory.
|
||||
SzInstanceName *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the parent instance specified in the counter path.
|
||||
//Is NULL, if the path does not specify a parent instance.
|
||||
//The string follows this structure in memory.
|
||||
SzParentInstance *uint16 // pointer to a string
|
||||
//Instance index specified in the counter path. Is 0, if the path does not specify an instance index.
|
||||
DwInstanceIndex uint32 // pointer to a string
|
||||
//Null-terminated string that contains the counter name. The string follows this structure in memory.
|
||||
SzCounterName *uint16 // pointer to a string
|
||||
//Help text that describes the counter. Is NULL if the source is a log file.
|
||||
SzExplainText *uint16 // pointer to a string
|
||||
//Start of the string data that is appended to the structure.
|
||||
DataBuffer [1]uint32 // pointer to an extra space
|
||||
}
|
||||
|
||||
// The pdhRawCounter structure returns the data as it was collected from the counter provider.
|
||||
// No translation, formatting, or other interpretation is performed on the data.
|
||||
type pdhRawCounter struct {
|
||||
// Counter status that indicates if the counter value is valid. Check this member before using the data in a calculation or displaying its value.
|
||||
// For a list of possible values, see https://docs.microsoft.com/windows/desktop/PerfCtrs/checking-pdh-interface-return-values
|
||||
CStatus uint32
|
||||
// Local time for when the data was collected
|
||||
TimeStamp fileTime
|
||||
// First raw counter value.
|
||||
FirstValue int64
|
||||
// Second raw counter value. Rate counters require two values in order to compute a displayable value.
|
||||
SecondValue int64
|
||||
// If the counter type contains the PERF_MULTI_COUNTER flag, this member contains the additional counter data used in the calculation.
|
||||
// For example, the PERF_100NSEC_MULTI_TIMER counter type contains the PERF_MULTI_COUNTER flag.
|
||||
MultiCount uint32
|
||||
}
|
||||
|
||||
type pdhRawCounterItem struct {
|
||||
// Pointer to a null-terminated string that specifies the instance name of the counter. The string is appended to the end of this structure.
|
||||
SzName *uint16
|
||||
//A pdhRawCounter structure that contains the raw counter value of the instance
|
||||
RawValue pdhRawCounter
|
||||
}
|
333
plugins/inputs/win_perf_counters/performance_query.go
Normal file
333
plugins/inputs/win_perf_counters/performance_query.go
Normal file
|
@ -0,0 +1,333 @@
|
|||
// Go API over pdh syscalls
|
||||
//go:build windows
|
||||
|
||||
package win_perf_counters
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Initial buffer size for return buffers
|
||||
const initialBufferSize = uint32(1024) // 1kB
|
||||
|
||||
var errBufferLimitReached = errors.New("buffer limit reached")
|
||||
|
||||
// counterValue is abstraction for pdhFmtCountervalueItemDouble
|
||||
type counterValue struct {
|
||||
instanceName string
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// performanceQuery provides wrappers around Windows performance counters API for easy usage in GO
|
||||
//
|
||||
//nolint:interfacebloat // conditionally allow to contain more methods
|
||||
type performanceQuery interface {
|
||||
open() error
|
||||
close() error
|
||||
addCounterToQuery(counterPath string) (pdhCounterHandle, error)
|
||||
addEnglishCounterToQuery(counterPath string) (pdhCounterHandle, error)
|
||||
getCounterPath(counterHandle pdhCounterHandle) (string, error)
|
||||
expandWildCardPath(counterPath string) ([]string, error)
|
||||
getFormattedCounterValueDouble(hCounter pdhCounterHandle) (float64, error)
|
||||
getRawCounterValue(hCounter pdhCounterHandle) (int64, error)
|
||||
getFormattedCounterArrayDouble(hCounter pdhCounterHandle) ([]counterValue, error)
|
||||
getRawCounterArray(hCounter pdhCounterHandle) ([]counterValue, error)
|
||||
collectData() error
|
||||
collectDataWithTime() (time.Time, error)
|
||||
isVistaOrNewer() bool
|
||||
}
|
||||
|
||||
type performanceQueryCreator interface {
|
||||
newPerformanceQuery(string, uint32) performanceQuery
|
||||
}
|
||||
|
||||
// pdhError represents error returned from Performance Counters API
|
||||
type pdhError struct {
|
||||
errorCode uint32
|
||||
errorText string
|
||||
}
|
||||
|
||||
func (m *pdhError) Error() string {
|
||||
return m.errorText
|
||||
}
|
||||
|
||||
func newPdhError(code uint32) error {
|
||||
return &pdhError{
|
||||
errorCode: code,
|
||||
errorText: pdhFormatError(code),
|
||||
}
|
||||
}
|
||||
|
||||
// performanceQueryImpl is implementation of performanceQuery interface, which calls phd.dll functions
|
||||
type performanceQueryImpl struct {
|
||||
maxBufferSize uint32
|
||||
query pdhQueryHandle
|
||||
}
|
||||
|
||||
type performanceQueryCreatorImpl struct{}
|
||||
|
||||
func (performanceQueryCreatorImpl) newPerformanceQuery(_ string, maxBufferSize uint32) performanceQuery {
|
||||
return &performanceQueryImpl{maxBufferSize: maxBufferSize}
|
||||
}
|
||||
|
||||
// open creates a new counterPath that is used to manage the collection of performance data.
|
||||
// It returns counterPath handle used for subsequent calls for adding counters and querying data
|
||||
func (m *performanceQueryImpl) open() error {
|
||||
if m.query != 0 {
|
||||
err := m.close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var handle pdhQueryHandle
|
||||
|
||||
if ret := pdhOpenQuery(0, 0, &handle); ret != errorSuccess {
|
||||
return newPdhError(ret)
|
||||
}
|
||||
m.query = handle
|
||||
return nil
|
||||
}
|
||||
|
||||
// close closes the counterPath, releases associated counter handles and frees resources
|
||||
func (m *performanceQueryImpl) close() error {
|
||||
if m.query == 0 {
|
||||
return errors.New("uninitialized query")
|
||||
}
|
||||
|
||||
if ret := pdhCloseQuery(m.query); ret != errorSuccess {
|
||||
return newPdhError(ret)
|
||||
}
|
||||
m.query = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *performanceQueryImpl) addCounterToQuery(counterPath string) (pdhCounterHandle, error) {
|
||||
var counterHandle pdhCounterHandle
|
||||
if m.query == 0 {
|
||||
return 0, errors.New("uninitialized query")
|
||||
}
|
||||
|
||||
if ret := pdhAddCounter(m.query, counterPath, 0, &counterHandle); ret != errorSuccess {
|
||||
return 0, newPdhError(ret)
|
||||
}
|
||||
return counterHandle, nil
|
||||
}
|
||||
|
||||
func (m *performanceQueryImpl) addEnglishCounterToQuery(counterPath string) (pdhCounterHandle, error) {
|
||||
var counterHandle pdhCounterHandle
|
||||
if m.query == 0 {
|
||||
return 0, errors.New("uninitialized query")
|
||||
}
|
||||
if ret := pdhAddEnglishCounter(m.query, counterPath, 0, &counterHandle); ret != errorSuccess {
|
||||
return 0, newPdhError(ret)
|
||||
}
|
||||
return counterHandle, nil
|
||||
}
|
||||
|
||||
// getCounterPath returns counter information for given handle
|
||||
func (m *performanceQueryImpl) getCounterPath(counterHandle pdhCounterHandle) (string, error) {
|
||||
for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 {
|
||||
buf := make([]byte, buflen)
|
||||
|
||||
// Get the info with the current buffer size
|
||||
size := buflen
|
||||
ret := pdhGetCounterInfo(counterHandle, 0, &size, &buf[0])
|
||||
if ret == errorSuccess {
|
||||
ci := (*pdhCounterInfo)(unsafe.Pointer(&buf[0])) //nolint:gosec // G103: Valid use of unsafe call to create PDH_COUNTER_INFO
|
||||
return utf16PtrToString(ci.SzFullPath), nil
|
||||
}
|
||||
|
||||
// Use the size as a hint if it exceeds the current buffer size
|
||||
if size > buflen {
|
||||
buflen = size
|
||||
}
|
||||
|
||||
// We got a non-recoverable error so exit here
|
||||
if ret != pdhMoreData {
|
||||
return "", newPdhError(ret)
|
||||
}
|
||||
}
|
||||
|
||||
return "", errBufferLimitReached
|
||||
}
|
||||
|
||||
// expandWildCardPath examines local computer and returns those counter paths that match the given counter path which contains wildcard characters.
|
||||
func (m *performanceQueryImpl) expandWildCardPath(counterPath string) ([]string, error) {
|
||||
for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 {
|
||||
buf := make([]uint16, buflen)
|
||||
|
||||
// Get the info with the current buffer size
|
||||
size := buflen
|
||||
ret := pdhExpandWildCardPath(counterPath, &buf[0], &size)
|
||||
if ret == errorSuccess {
|
||||
return utf16ToStringArray(buf), nil
|
||||
}
|
||||
|
||||
// Use the size as a hint if it exceeds the current buffer size
|
||||
if size > buflen {
|
||||
buflen = size
|
||||
}
|
||||
|
||||
// We got a non-recoverable error so exit here
|
||||
if ret != pdhMoreData {
|
||||
return nil, newPdhError(ret)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errBufferLimitReached
|
||||
}
|
||||
|
||||
// getFormattedCounterValueDouble computes a displayable value for the specified counter
|
||||
func (*performanceQueryImpl) getFormattedCounterValueDouble(hCounter pdhCounterHandle) (float64, error) {
|
||||
var counterType uint32
|
||||
var value pdhFmtCountervalueDouble
|
||||
|
||||
if ret := pdhGetFormattedCounterValueDouble(hCounter, &counterType, &value); ret != errorSuccess {
|
||||
return 0, newPdhError(ret)
|
||||
}
|
||||
if value.CStatus == pdhCstatusValidData || value.CStatus == pdhCstatusNewData {
|
||||
return value.DoubleValue, nil
|
||||
}
|
||||
return 0, newPdhError(value.CStatus)
|
||||
}
|
||||
|
||||
func (m *performanceQueryImpl) getFormattedCounterArrayDouble(hCounter pdhCounterHandle) ([]counterValue, error) {
|
||||
for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 {
|
||||
buf := make([]byte, buflen)
|
||||
|
||||
// Get the info with the current buffer size
|
||||
var itemCount uint32
|
||||
size := buflen
|
||||
ret := pdhGetFormattedCounterArrayDouble(hCounter, &size, &itemCount, &buf[0])
|
||||
if ret == errorSuccess {
|
||||
//nolint:gosec // G103: Valid use of unsafe call to create PDH_FMT_COUNTERVALUE_ITEM_DOUBLE
|
||||
items := (*[1 << 20]pdhFmtCountervalueItemDouble)(unsafe.Pointer(&buf[0]))[:itemCount]
|
||||
values := make([]counterValue, 0, itemCount)
|
||||
for _, item := range items {
|
||||
if item.FmtValue.CStatus == pdhCstatusValidData || item.FmtValue.CStatus == pdhCstatusNewData {
|
||||
val := counterValue{utf16PtrToString(item.SzName), item.FmtValue.DoubleValue}
|
||||
values = append(values, val)
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// Use the size as a hint if it exceeds the current buffer size
|
||||
if size > buflen {
|
||||
buflen = size
|
||||
}
|
||||
|
||||
// We got a non-recoverable error so exit here
|
||||
if ret != pdhMoreData {
|
||||
return nil, newPdhError(ret)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errBufferLimitReached
|
||||
}
|
||||
|
||||
func (m *performanceQueryImpl) getRawCounterArray(hCounter pdhCounterHandle) ([]counterValue, error) {
|
||||
for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 {
|
||||
buf := make([]byte, buflen)
|
||||
|
||||
// Get the info with the current buffer size
|
||||
var itemCount uint32
|
||||
size := buflen
|
||||
ret := pdhGetRawCounterArray(hCounter, &size, &itemCount, &buf[0])
|
||||
if ret == errorSuccess {
|
||||
//nolint:gosec // G103: Valid use of unsafe call to create PDH_RAW_COUNTER_ITEM
|
||||
items := (*[1 << 20]pdhRawCounterItem)(unsafe.Pointer(&buf[0]))[:itemCount]
|
||||
values := make([]counterValue, 0, itemCount)
|
||||
for _, item := range items {
|
||||
if item.RawValue.CStatus == pdhCstatusValidData || item.RawValue.CStatus == pdhCstatusNewData {
|
||||
val := counterValue{utf16PtrToString(item.SzName), item.RawValue.FirstValue}
|
||||
values = append(values, val)
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// Use the size as a hint if it exceeds the current buffer size
|
||||
if size > buflen {
|
||||
buflen = size
|
||||
}
|
||||
|
||||
// We got a non-recoverable error so exit here
|
||||
if ret != pdhMoreData {
|
||||
return nil, newPdhError(ret)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errBufferLimitReached
|
||||
}
|
||||
|
||||
func (m *performanceQueryImpl) collectData() error {
|
||||
var ret uint32
|
||||
if m.query == 0 {
|
||||
return errors.New("uninitialized query")
|
||||
}
|
||||
|
||||
if ret = pdhCollectQueryData(m.query); ret != errorSuccess {
|
||||
return newPdhError(ret)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *performanceQueryImpl) collectDataWithTime() (time.Time, error) {
|
||||
if m.query == 0 {
|
||||
return time.Now(), errors.New("uninitialized query")
|
||||
}
|
||||
ret, mtime := pdhCollectQueryDataWithTime(m.query)
|
||||
if ret != errorSuccess {
|
||||
return time.Now(), newPdhError(ret)
|
||||
}
|
||||
return mtime, nil
|
||||
}
|
||||
|
||||
func (*performanceQueryImpl) isVistaOrNewer() bool {
|
||||
return pdhAddEnglishCounterSupported()
|
||||
}
|
||||
|
||||
func (m *performanceQueryImpl) getRawCounterValue(hCounter pdhCounterHandle) (int64, error) {
|
||||
if m.query == 0 {
|
||||
return 0, errors.New("uninitialised query")
|
||||
}
|
||||
|
||||
var counterType uint32
|
||||
var value pdhRawCounter
|
||||
var ret uint32
|
||||
|
||||
if ret = pdhGetRawCounterValue(hCounter, &counterType, &value); ret == errorSuccess {
|
||||
if value.CStatus == pdhCstatusValidData || value.CStatus == pdhCstatusNewData {
|
||||
return value.FirstValue, nil
|
||||
}
|
||||
return 0, newPdhError(value.CStatus)
|
||||
}
|
||||
return 0, newPdhError(ret)
|
||||
}
|
||||
|
||||
// utf16PtrToString converts Windows API LPTSTR (pointer to string) to go string
|
||||
func utf16PtrToString(s *uint16) string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
//nolint:gosec // G103: Valid use of unsafe call to create string from Windows API LPTSTR (pointer to string)
|
||||
return syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(s))[0:])
|
||||
}
|
||||
|
||||
// utf16ToStringArray converts list of Windows API NULL terminated strings to go string array
|
||||
func utf16ToStringArray(buf []uint16) []string {
|
||||
var strings []string
|
||||
nextLineStart := 0
|
||||
stringLine := utf16PtrToString(&buf[0])
|
||||
for stringLine != "" {
|
||||
strings = append(strings, stringLine)
|
||||
nextLineStart += len([]rune(stringLine)) + 1
|
||||
remainingBuf := buf[nextLineStart:]
|
||||
stringLine = utf16PtrToString(&remainingBuf[0])
|
||||
}
|
||||
return strings
|
||||
}
|
170
plugins/inputs/win_perf_counters/sample.conf
Normal file
170
plugins/inputs/win_perf_counters/sample.conf
Normal file
|
@ -0,0 +1,170 @@
|
|||
# Input plugin to counterPath Performance Counters on Windows operating systems
|
||||
# This plugin ONLY supports Windows
|
||||
[[inputs.win_perf_counters]]
|
||||
## 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.
|
||||
|
||||
## Print All matching performance counters
|
||||
# PrintValid = false
|
||||
|
||||
## Whether request a timestamp along with the PerfCounter data or use current
|
||||
## time
|
||||
# UsePerfCounterTime = true
|
||||
|
||||
## 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 and in case of localized Windows, counter
|
||||
## paths will be also localized. It also returns instance indexes in instance
|
||||
## names. If false, wildcards (not partial) in instance names will still be
|
||||
## expanded, but instance indexes will not be returned in instance names.
|
||||
# UseWildcardsExpansion = false
|
||||
|
||||
## 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
|
||||
|
||||
## Period after which counters will be reread from configuration and
|
||||
## wildcards in counter paths expanded
|
||||
# CountersRefreshInterval="1m"
|
||||
|
||||
## 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 = []
|
||||
|
||||
## Maximum size of the buffer for values returned by the API
|
||||
## Increase this value if you experience "buffer limit reached" errors.
|
||||
# MaxBufferSize = "4MiB"
|
||||
|
||||
## NOTE: Due to the way TOML is parsed, tables must be at the END of the
|
||||
## plugin definition, otherwise additional config options are read as part of
|
||||
## the table
|
||||
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = ""
|
||||
# ObjectName = ""
|
||||
# Instances = [""]
|
||||
# Counters = []
|
||||
## Additional Object Settings
|
||||
## * IncludeTotal: set to true to include _Total instance when querying
|
||||
## for all metrics via '*'
|
||||
## * WarnOnMissing: print out when the performance counter is missing
|
||||
## from object, counter or instance
|
||||
## * UseRawValues: gather raw values instead of formatted. Raw values are
|
||||
## stored in the field name with the "_Raw" suffix, e.g.
|
||||
## "Disk_Read_Bytes_sec_Raw".
|
||||
# IncludeTotal = false
|
||||
# WarnOnMissing = false
|
||||
# UseRawValues = false
|
||||
|
||||
## Processor usage, alternative to native, reports on a per core.
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = "win_cpu"
|
||||
# ObjectName = "Processor"
|
||||
# Instances = ["*"]
|
||||
# UseRawValues = true
|
||||
# Counters = [
|
||||
# "% Idle Time",
|
||||
# "% Interrupt Time",
|
||||
# "% Privileged Time",
|
||||
# "% User Time",
|
||||
# "% Processor Time",
|
||||
# "% DPC Time",
|
||||
# ]
|
||||
|
||||
## Disk times and queues
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = "win_disk"
|
||||
# ObjectName = "LogicalDisk"
|
||||
# Instances = ["*"]
|
||||
# Counters = [
|
||||
# "% Idle Time",
|
||||
# "% Disk Time",
|
||||
# "% Disk Read Time",
|
||||
# "% Disk Write Time",
|
||||
# "% User Time",
|
||||
# "% Free Space",
|
||||
# "Current Disk Queue Length",
|
||||
# "Free Megabytes",
|
||||
# ]
|
||||
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = "win_diskio"
|
||||
# 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",
|
||||
# ]
|
||||
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = "win_net"
|
||||
# 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",
|
||||
# ]
|
||||
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = "win_system"
|
||||
# ObjectName = "System"
|
||||
# Instances = ["------"]
|
||||
# Counters = [
|
||||
# "Context Switches/sec",
|
||||
# "System Calls/sec",
|
||||
# "Processor Queue Length",
|
||||
# "System Up Time",
|
||||
# ]
|
||||
|
||||
## Example counterPath where the Instance portion must be removed to get
|
||||
## data back, such as from the Memory object.
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = "win_mem"
|
||||
# ObjectName = "Memory"
|
||||
## Use 6 x - to remove the Instance bit from the counterPath.
|
||||
# Instances = ["------"]
|
||||
# Counters = [
|
||||
# "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",
|
||||
# ]
|
||||
|
||||
## Example query where the Instance portion must be removed to get data back,
|
||||
## such as from the Paging File object.
|
||||
# [[inputs.win_perf_counters.object]]
|
||||
# Measurement = "win_swap"
|
||||
# ObjectName = "Paging File"
|
||||
# Instances = ["_Total"]
|
||||
# Counters = [
|
||||
# "% Usage",
|
||||
# ]
|
633
plugins/inputs/win_perf_counters/win_perf_counters.go
Normal file
633
plugins/inputs/win_perf_counters/win_perf_counters.go
Normal file
|
@ -0,0 +1,633 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
//go:build windows
|
||||
|
||||
package win_perf_counters
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
var (
|
||||
defaultMaxBufferSize = config.Size(100 * 1024 * 1024)
|
||||
sanitizedChars = strings.NewReplacer("/sec", "_persec", "/Sec", "_persec", " ", "_", "%", "Percent", `\`, "")
|
||||
)
|
||||
|
||||
const emptyInstance = "------"
|
||||
|
||||
type WinPerfCounters struct {
|
||||
PrintValid bool `toml:"PrintValid"`
|
||||
PreVistaSupport bool `toml:"PreVistaSupport" deprecated:"1.7.0;1.35.0;determined dynamically"`
|
||||
UsePerfCounterTime bool `toml:"UsePerfCounterTime"`
|
||||
Object []perfObject `toml:"object"`
|
||||
CountersRefreshInterval config.Duration `toml:"CountersRefreshInterval"`
|
||||
UseWildcardsExpansion bool `toml:"UseWildcardsExpansion"`
|
||||
LocalizeWildcardsExpansion bool `toml:"LocalizeWildcardsExpansion"`
|
||||
IgnoredErrors []string `toml:"IgnoredErrors"`
|
||||
MaxBufferSize config.Size `toml:"MaxBufferSize"`
|
||||
Sources []string `toml:"Sources"`
|
||||
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
|
||||
lastRefreshed time.Time
|
||||
queryCreator performanceQueryCreator
|
||||
hostCounters map[string]*hostCountersInfo
|
||||
// cached os.Hostname()
|
||||
cachedHostname string
|
||||
}
|
||||
|
||||
type perfObject struct {
|
||||
Sources []string `toml:"Sources"`
|
||||
ObjectName string `toml:"ObjectName"`
|
||||
Counters []string `toml:"Counters"`
|
||||
Instances []string `toml:"Instances"`
|
||||
Measurement string `toml:"Measurement"`
|
||||
WarnOnMissing bool `toml:"WarnOnMissing"`
|
||||
FailOnMissing bool `toml:"FailOnMissing"`
|
||||
IncludeTotal bool `toml:"IncludeTotal"`
|
||||
UseRawValues bool `toml:"UseRawValues"`
|
||||
}
|
||||
|
||||
type hostCountersInfo struct {
|
||||
// computer name used as key and for printing
|
||||
computer string
|
||||
// computer name used in tag
|
||||
tag string
|
||||
counters []*counter
|
||||
query performanceQuery
|
||||
timestamp time.Time
|
||||
}
|
||||
|
||||
type counter struct {
|
||||
counterPath string
|
||||
computer string
|
||||
objectName string
|
||||
counter string
|
||||
instance string
|
||||
measurement string
|
||||
includeTotal bool
|
||||
useRawValue bool
|
||||
counterHandle pdhCounterHandle
|
||||
}
|
||||
|
||||
type instanceGrouping struct {
|
||||
name string
|
||||
instance string
|
||||
objectName string
|
||||
}
|
||||
|
||||
type fieldGrouping map[instanceGrouping]map[string]interface{}
|
||||
|
||||
func (*WinPerfCounters) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (m *WinPerfCounters) Init() error {
|
||||
// Check the buffer size
|
||||
if m.MaxBufferSize < config.Size(initialBufferSize) {
|
||||
return fmt.Errorf("maximum buffer size should at least be %d", 2*initialBufferSize)
|
||||
}
|
||||
if m.MaxBufferSize > math.MaxUint32 {
|
||||
return fmt.Errorf("maximum buffer size should be smaller than %d", uint32(math.MaxUint32))
|
||||
}
|
||||
|
||||
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 errors.New("wildcards can't be used with LocalizeWildcardsExpansion=false")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *WinPerfCounters) Gather(acc telegraf.Accumulator) error {
|
||||
// Parse the config once
|
||||
var err error
|
||||
|
||||
if m.lastRefreshed.IsZero() || (m.CountersRefreshInterval > 0 && m.lastRefreshed.Add(time.Duration(m.CountersRefreshInterval)).Before(time.Now())) {
|
||||
if err := m.cleanQueries(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.parseConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, hostCounterSet := range m.hostCounters {
|
||||
// some counters need two data samples before computing a value
|
||||
if err = hostCounterSet.query.collectData(); err != nil {
|
||||
return m.checkError(err)
|
||||
}
|
||||
}
|
||||
m.lastRefreshed = time.Now()
|
||||
// minimum time between collecting two samples
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
for _, hostCounterSet := range m.hostCounters {
|
||||
if m.UsePerfCounterTime && hostCounterSet.query.isVistaOrNewer() {
|
||||
hostCounterSet.timestamp, err = hostCounterSet.query.collectDataWithTime()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
hostCounterSet.timestamp = time.Now()
|
||||
if err := hostCounterSet.query.collectData(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
// iterate over computers
|
||||
for _, hostCounterInfo := range m.hostCounters {
|
||||
wg.Add(1)
|
||||
go func(hostInfo *hostCountersInfo) {
|
||||
m.Log.Debugf("Gathering from %s", hostInfo.computer)
|
||||
start := time.Now()
|
||||
err := m.gatherComputerCounters(hostInfo, acc)
|
||||
m.Log.Debugf("Gathering from %s finished in %v", hostInfo.computer, time.Since(start))
|
||||
if err != nil && m.checkError(err) != nil {
|
||||
acc.AddError(fmt.Errorf("error during collecting data on host %q: %w", hostInfo.computer, err))
|
||||
}
|
||||
wg.Done()
|
||||
}(hostCounterInfo)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
//
|
||||
//nolint:revive //function-result-limit conditionally 5 return results allowed
|
||||
func extractCounterInfoFromCounterPath(counterPath string) (computer string, object string, instance string, counter string, err error) {
|
||||
leftComputerBorderIndex := -1
|
||||
rightObjectBorderIndex := -1
|
||||
leftObjectBorderIndex := -1
|
||||
leftCounterBorderIndex := -1
|
||||
rightInstanceBorderIndex := -1
|
||||
leftInstanceBorderIndex := -1
|
||||
var bracketLevel int
|
||||
|
||||
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
|
||||
} else if leftComputerBorderIndex == -1 {
|
||||
leftComputerBorderIndex = 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 {
|
||||
return "", "", "", "", errors.New("cannot parse object from: " + counterPath)
|
||||
}
|
||||
|
||||
if leftComputerBorderIndex > -1 {
|
||||
// validate there is leading \\ and not empty computer (\\\O)
|
||||
if leftComputerBorderIndex != 1 || leftComputerBorderIndex == leftObjectBorderIndex-1 {
|
||||
return "", "", "", "", errors.New("cannot parse computer from: " + counterPath)
|
||||
}
|
||||
computer = counterPath[leftComputerBorderIndex+1 : leftObjectBorderIndex]
|
||||
}
|
||||
|
||||
if leftInstanceBorderIndex > -1 && rightInstanceBorderIndex > -1 {
|
||||
instance = counterPath[leftInstanceBorderIndex+1 : rightInstanceBorderIndex]
|
||||
} else if (leftInstanceBorderIndex == -1 && rightInstanceBorderIndex > -1) || (leftInstanceBorderIndex > -1 && rightInstanceBorderIndex == -1) {
|
||||
return "", "", "", "", errors.New("cannot parse instance from: " + counterPath)
|
||||
}
|
||||
object = counterPath[leftObjectBorderIndex+1 : rightObjectBorderIndex]
|
||||
counter = counterPath[leftCounterBorderIndex+1:]
|
||||
return computer, object, instance, counter, nil
|
||||
}
|
||||
|
||||
func (m *WinPerfCounters) hostname() string {
|
||||
if m.cachedHostname != "" {
|
||||
return m.cachedHostname
|
||||
}
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
m.cachedHostname = "localhost"
|
||||
} else {
|
||||
m.cachedHostname = hostname
|
||||
}
|
||||
return m.cachedHostname
|
||||
}
|
||||
|
||||
//nolint:revive //argument-limit conditionally more arguments allowed for helper function
|
||||
func newCounter(
|
||||
counterHandle pdhCounterHandle,
|
||||
counterPath string,
|
||||
computer 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, computer, objectName, newCounterName, instance, measurementName,
|
||||
includeTotal, useRawValue, counterHandle}
|
||||
}
|
||||
|
||||
//nolint:revive //argument-limit conditionally more arguments allowed
|
||||
func (m *WinPerfCounters) addItem(counterPath, computer, objectName, instance, counterName, measurement string, includeTotal bool, useRawValue bool) error {
|
||||
origCounterPath := counterPath
|
||||
var err error
|
||||
var counterHandle pdhCounterHandle
|
||||
|
||||
sourceTag := computer
|
||||
if computer == "localhost" {
|
||||
sourceTag = m.hostname()
|
||||
}
|
||||
if m.hostCounters == nil {
|
||||
m.hostCounters = make(map[string]*hostCountersInfo)
|
||||
}
|
||||
hostCounter, ok := m.hostCounters[computer]
|
||||
if !ok {
|
||||
hostCounter = &hostCountersInfo{computer: computer, tag: sourceTag}
|
||||
m.hostCounters[computer] = hostCounter
|
||||
hostCounter.query = m.queryCreator.newPerformanceQuery(computer, uint32(m.MaxBufferSize))
|
||||
if err := hostCounter.query.open(); err != nil {
|
||||
return err
|
||||
}
|
||||
hostCounter.counters = make([]*counter, 0)
|
||||
}
|
||||
|
||||
if !hostCounter.query.isVistaOrNewer() {
|
||||
counterHandle, err = hostCounter.query.addCounterToQuery(counterPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
counterHandle, err = hostCounter.query.addEnglishCounterToQuery(counterPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if m.UseWildcardsExpansion {
|
||||
origInstance := instance
|
||||
counterPath, err = hostCounter.query.getCounterPath(counterHandle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
counters, err := hostCounter.query.expandWildCardPath(counterPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, origObjectName, _, origCounterName, err := extractCounterInfoFromCounterPath(origCounterPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, counterPath := range counters {
|
||||
_, err := hostCounter.query.addCounterToQuery(counterPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
computer, objectName, instance, counterName, err = extractCounterInfoFromCounterPath(counterPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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(computer, origObjectName, newInstance, origCounterName)
|
||||
counterHandle, err = hostCounter.query.addEnglishCounterToQuery(counterPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newItem = newCounter(
|
||||
counterHandle,
|
||||
counterPath,
|
||||
computer,
|
||||
origObjectName, instance,
|
||||
origCounterName,
|
||||
measurement,
|
||||
includeTotal,
|
||||
useRawValue,
|
||||
)
|
||||
} else {
|
||||
counterHandle, err = hostCounter.query.addCounterToQuery(counterPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newItem = newCounter(
|
||||
counterHandle,
|
||||
counterPath,
|
||||
computer,
|
||||
objectName,
|
||||
instance,
|
||||
counterName,
|
||||
measurement,
|
||||
includeTotal,
|
||||
useRawValue,
|
||||
)
|
||||
}
|
||||
|
||||
if instance == "_Total" && origInstance == "*" && !includeTotal {
|
||||
continue
|
||||
}
|
||||
|
||||
hostCounter.counters = append(hostCounter.counters, newItem)
|
||||
|
||||
if m.PrintValid {
|
||||
m.Log.Infof("Valid: %s", counterPath)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newItem := newCounter(
|
||||
counterHandle,
|
||||
counterPath,
|
||||
computer,
|
||||
objectName,
|
||||
instance,
|
||||
counterName,
|
||||
measurement,
|
||||
includeTotal,
|
||||
useRawValue,
|
||||
)
|
||||
hostCounter.counters = append(hostCounter.counters, newItem)
|
||||
if m.PrintValid {
|
||||
m.Log.Infof("Valid: %s", counterPath)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatPath(computer, objectName, instance, counter string) string {
|
||||
path := ""
|
||||
if instance == emptyInstance {
|
||||
path = fmt.Sprintf(`\%s\%s`, objectName, counter)
|
||||
} else {
|
||||
path = fmt.Sprintf(`\%s(%s)\%s`, objectName, instance, counter)
|
||||
}
|
||||
if computer != "" && computer != "localhost" {
|
||||
path = fmt.Sprintf(`\\%s%s`, computer, path)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func (m *WinPerfCounters) parseConfig() error {
|
||||
var counterPath string
|
||||
|
||||
if len(m.Sources) == 0 {
|
||||
m.Sources = []string{"localhost"}
|
||||
}
|
||||
|
||||
if len(m.Object) == 0 {
|
||||
err := errors.New("no performance objects configured")
|
||||
return err
|
||||
}
|
||||
|
||||
for _, PerfObject := range m.Object {
|
||||
computers := PerfObject.Sources
|
||||
if len(computers) == 0 {
|
||||
computers = m.Sources
|
||||
}
|
||||
for _, computer := range computers {
|
||||
if computer == "" {
|
||||
// localhost as a computer name in counter path doesn't work
|
||||
computer = "localhost"
|
||||
}
|
||||
for _, counter := range PerfObject.Counters {
|
||||
if len(PerfObject.Instances) == 0 {
|
||||
m.Log.Warnf("Missing 'Instances' param for object %q", PerfObject.ObjectName)
|
||||
}
|
||||
for _, instance := range PerfObject.Instances {
|
||||
objectName := PerfObject.ObjectName
|
||||
counterPath = formatPath(computer, objectName, instance, counter)
|
||||
|
||||
err := m.addItem(counterPath, computer, objectName, instance, counter,
|
||||
PerfObject.Measurement, PerfObject.IncludeTotal, PerfObject.UseRawValues)
|
||||
if err != nil {
|
||||
if PerfObject.FailOnMissing || PerfObject.WarnOnMissing {
|
||||
m.Log.Errorf("Invalid counterPath %q: %s", counterPath, err.Error())
|
||||
}
|
||||
if PerfObject.FailOnMissing {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *WinPerfCounters) checkError(err error) error {
|
||||
var pdhErr *pdhError
|
||||
if errors.As(err, &pdhErr) {
|
||||
for _, ignoredErrors := range m.IgnoredErrors {
|
||||
if pdhErrors[pdhErr.errorCode] == ignoredErrors {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *WinPerfCounters) gatherComputerCounters(hostCounterInfo *hostCountersInfo, acc telegraf.Accumulator) error {
|
||||
var value interface{}
|
||||
var err error
|
||||
collectedFields := make(fieldGrouping)
|
||||
// For iterate over the known metrics and get the samples.
|
||||
for _, metric := range hostCounterInfo.counters {
|
||||
// collect
|
||||
if m.UseWildcardsExpansion {
|
||||
if metric.useRawValue {
|
||||
value, err = hostCounterInfo.query.getRawCounterValue(metric.counterHandle)
|
||||
} else {
|
||||
value, err = hostCounterInfo.query.getFormattedCounterValueDouble(metric.counterHandle)
|
||||
}
|
||||
if err != nil {
|
||||
// ignore invalid data as some counters from process instances returns this sometimes
|
||||
if !isKnownCounterDataError(err) {
|
||||
return fmt.Errorf("error while getting value for counter %q: %w", metric.counterPath, err)
|
||||
}
|
||||
m.Log.Warnf("Error while getting value for counter %q, instance: %s, will skip metric: %v", metric.counterPath, metric.instance, err)
|
||||
continue
|
||||
}
|
||||
addCounterMeasurement(metric, metric.instance, value, collectedFields)
|
||||
} else {
|
||||
var counterValues []counterValue
|
||||
if metric.useRawValue {
|
||||
counterValues, err = hostCounterInfo.query.getRawCounterArray(metric.counterHandle)
|
||||
} else {
|
||||
counterValues, err = hostCounterInfo.query.getFormattedCounterArrayDouble(metric.counterHandle)
|
||||
}
|
||||
if err != nil {
|
||||
// ignore invalid data as some counters from process instances returns this sometimes
|
||||
if !isKnownCounterDataError(err) {
|
||||
return fmt.Errorf("error while getting value for counter %q: %w", metric.counterPath, err)
|
||||
}
|
||||
m.Log.Warnf("Error while getting value for counter %q, instance: %s, will skip metric: %v", metric.counterPath, metric.instance, 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, collectedFields)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for instance, fields := range collectedFields {
|
||||
var tags = map[string]string{
|
||||
"objectname": instance.objectName,
|
||||
}
|
||||
if len(instance.instance) > 0 {
|
||||
tags["instance"] = instance.instance
|
||||
}
|
||||
if len(hostCounterInfo.tag) > 0 {
|
||||
tags["source"] = hostCounterInfo.tag
|
||||
}
|
||||
acc.AddFields(instance.name, fields, tags, hostCounterInfo.timestamp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *WinPerfCounters) cleanQueries() error {
|
||||
for _, hostCounterInfo := range m.hostCounters {
|
||||
if err := hostCounterInfo.query.close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
m.hostCounters = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
if metric.instance == emptyInstance {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func addCounterMeasurement(metric *counter, instanceName string, value interface{}, collectFields fieldGrouping) {
|
||||
var instance = instanceGrouping{metric.measurement, instanceName, metric.objectName}
|
||||
if collectFields[instance] == nil {
|
||||
collectFields[instance] = make(map[string]interface{})
|
||||
}
|
||||
collectFields[instance][sanitizedChars.Replace(metric.counter)] = value
|
||||
}
|
||||
|
||||
func isKnownCounterDataError(err error) bool {
|
||||
var pdhErr *pdhError
|
||||
if errors.As(err, &pdhErr) && (pdhErr.errorCode == pdhInvalidData ||
|
||||
pdhErr.errorCode == pdhCalcNegativeDenominator ||
|
||||
pdhErr.errorCode == pdhCalcNegativeValue ||
|
||||
pdhErr.errorCode == pdhCstatusInvalidData ||
|
||||
pdhErr.errorCode == pdhCstatusNoInstance ||
|
||||
pdhErr.errorCode == pdhNoData) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("win_perf_counters", func() telegraf.Input {
|
||||
return &WinPerfCounters{
|
||||
CountersRefreshInterval: config.Duration(time.Second * 60),
|
||||
LocalizeWildcardsExpansion: true,
|
||||
MaxBufferSize: defaultMaxBufferSize,
|
||||
queryCreator: &performanceQueryCreatorImpl{},
|
||||
}
|
||||
})
|
||||
}
|
|
@ -0,0 +1,563 @@
|
|||
//go:build windows
|
||||
|
||||
package win_perf_counters
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func TestWinPerformanceQueryImplIntegration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
query := &performanceQueryImpl{maxBufferSize: uint32(defaultMaxBufferSize)}
|
||||
|
||||
err := query.close()
|
||||
require.Error(t, err, "uninitialized query must return errors")
|
||||
|
||||
_, err = query.addCounterToQuery("")
|
||||
require.Error(t, err, "uninitialized query must return errors")
|
||||
require.ErrorContains(t, err, "uninitialized")
|
||||
|
||||
_, err = query.addEnglishCounterToQuery("")
|
||||
require.Error(t, err, "uninitialized query must return errors")
|
||||
require.ErrorContains(t, err, "uninitialized")
|
||||
|
||||
err = query.collectData()
|
||||
require.Error(t, err, "uninitialized query must return errors")
|
||||
require.ErrorContains(t, err, "uninitialized")
|
||||
|
||||
require.NoError(t, query.open())
|
||||
|
||||
counterPath := "\\Processor Information(_Total)\\% Processor Time"
|
||||
|
||||
hCounter, err := query.addCounterToQuery(counterPath)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, 0, hCounter)
|
||||
|
||||
require.NoError(t, query.close())
|
||||
|
||||
require.NoError(t, query.open())
|
||||
|
||||
hCounter, err = query.addEnglishCounterToQuery(counterPath)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, 0, hCounter)
|
||||
|
||||
cp, err := query.getCounterPath(hCounter)
|
||||
require.NoError(t, err)
|
||||
require.True(t, strings.HasSuffix(cp, counterPath))
|
||||
|
||||
require.NoError(t, query.collectData())
|
||||
time.Sleep(time.Second)
|
||||
|
||||
require.NoError(t, query.collectData())
|
||||
|
||||
fcounter, err := query.getFormattedCounterValueDouble(hCounter)
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, fcounter, float64(0))
|
||||
|
||||
rcounter, err := query.getRawCounterValue(hCounter)
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rcounter, int64(10000000))
|
||||
|
||||
now := time.Now()
|
||||
mtime, err := query.collectDataWithTime()
|
||||
require.NoError(t, err)
|
||||
require.Less(t, mtime.Sub(now), time.Second)
|
||||
|
||||
counterPath = "\\Process(*)\\% Processor Time"
|
||||
paths, err := query.expandWildCardPath(counterPath)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, paths)
|
||||
require.Greater(t, len(paths), 1)
|
||||
|
||||
counterPath = "\\Process(_Total)\\*"
|
||||
paths, err = query.expandWildCardPath(counterPath)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, paths)
|
||||
require.Greater(t, len(paths), 1)
|
||||
|
||||
require.NoError(t, query.open())
|
||||
|
||||
counterPath = "\\Process(*)\\% Processor Time"
|
||||
hCounter, err = query.addEnglishCounterToQuery(counterPath)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, 0, hCounter)
|
||||
|
||||
require.NoError(t, query.collectData())
|
||||
time.Sleep(time.Second)
|
||||
|
||||
require.NoError(t, query.collectData())
|
||||
|
||||
farr, err := query.getFormattedCounterArrayDouble(hCounter)
|
||||
var phdErr *pdhError
|
||||
if errors.As(err, &phdErr) && phdErr.errorCode != pdhInvalidData && phdErr.errorCode != pdhCalcNegativeValue {
|
||||
time.Sleep(time.Second)
|
||||
farr, err = query.getFormattedCounterArrayDouble(hCounter)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, farr)
|
||||
|
||||
rarr, err := query.getRawCounterArray(hCounter)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, rarr, "Too")
|
||||
|
||||
require.NoError(t, query.close())
|
||||
}
|
||||
|
||||
func TestWinPerfCountersConfigGet1Integration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
instances := []string{"_Total"}
|
||||
counters := []string{"% Processor Time"}
|
||||
perfObjects := []perfObject{{
|
||||
ObjectName: "Processor Information",
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: "test",
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}}
|
||||
|
||||
m := WinPerfCounters{
|
||||
PrintValid: false,
|
||||
Object: perfObjects,
|
||||
MaxBufferSize: defaultMaxBufferSize,
|
||||
Log: testutil.Logger{},
|
||||
queryCreator: &performanceQueryCreatorImpl{},
|
||||
}
|
||||
|
||||
require.NoError(t, m.parseConfig())
|
||||
}
|
||||
|
||||
func TestWinPerfCountersConfigGet2Integration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
instances := []string{"_Total"}
|
||||
counters := []string{"% Processor Time"}
|
||||
perfObjects := []perfObject{{
|
||||
ObjectName: "Processor Information",
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: "test",
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}}
|
||||
|
||||
m := WinPerfCounters{
|
||||
PrintValid: false,
|
||||
Object: perfObjects,
|
||||
MaxBufferSize: defaultMaxBufferSize,
|
||||
Log: testutil.Logger{},
|
||||
queryCreator: &performanceQueryCreatorImpl{},
|
||||
}
|
||||
|
||||
require.NoError(t, m.parseConfig())
|
||||
|
||||
hostCounters, ok := m.hostCounters["localhost"]
|
||||
require.True(t, ok)
|
||||
require.Len(t, hostCounters.counters, 1, "There should be exactly one result returned from the counterPath")
|
||||
}
|
||||
|
||||
func TestWinPerfCountersConfigGet3Integration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
sources := []string{"localhost"}
|
||||
instances := []string{"_Total"}
|
||||
counters := []string{"% Processor Time", "% Idle Time"}
|
||||
perfObjects := []perfObject{{
|
||||
Sources: sources,
|
||||
ObjectName: "Processor Information",
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: "test",
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}}
|
||||
|
||||
m := WinPerfCounters{
|
||||
PrintValid: false,
|
||||
Object: perfObjects,
|
||||
MaxBufferSize: defaultMaxBufferSize,
|
||||
Log: testutil.Logger{},
|
||||
queryCreator: &performanceQueryCreatorImpl{},
|
||||
}
|
||||
|
||||
require.NoError(t, m.parseConfig())
|
||||
|
||||
hostCounters, ok := m.hostCounters["localhost"]
|
||||
require.True(t, ok)
|
||||
require.Len(t, hostCounters.counters, 2, "There should be exactly two results returned from the counterPath")
|
||||
}
|
||||
|
||||
func TestWinPerfCountersConfigGet4Integration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
instances := []string{"_Total", "0,1"}
|
||||
counters := []string{"% Processor Time"}
|
||||
perfObjects := []perfObject{{
|
||||
ObjectName: "Processor Information",
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: "test",
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}}
|
||||
|
||||
m := WinPerfCounters{
|
||||
PrintValid: false,
|
||||
Object: perfObjects,
|
||||
MaxBufferSize: defaultMaxBufferSize,
|
||||
Log: testutil.Logger{},
|
||||
queryCreator: &performanceQueryCreatorImpl{},
|
||||
}
|
||||
|
||||
require.NoError(t, m.parseConfig())
|
||||
|
||||
hostCounters, ok := m.hostCounters["localhost"]
|
||||
require.True(t, ok)
|
||||
require.Len(t, hostCounters.counters, 2, "There should be exactly two results returned from the counterPath")
|
||||
}
|
||||
|
||||
func TestWinPerfCountersConfigGet5Integration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
instances := []string{"_Total", "0,1"}
|
||||
counters := []string{"% Processor Time", "% Idle Time"}
|
||||
perfObjects := []perfObject{{
|
||||
ObjectName: "Processor Information",
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: "test",
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}}
|
||||
|
||||
m := WinPerfCounters{
|
||||
PrintValid: false,
|
||||
Object: perfObjects,
|
||||
MaxBufferSize: defaultMaxBufferSize,
|
||||
Log: testutil.Logger{},
|
||||
queryCreator: &performanceQueryCreatorImpl{},
|
||||
}
|
||||
|
||||
require.NoError(t, m.parseConfig())
|
||||
|
||||
hostCounters, ok := m.hostCounters["localhost"]
|
||||
require.True(t, ok)
|
||||
require.Len(t, hostCounters.counters, 4, "There should be exactly four results returned from the counterPath")
|
||||
}
|
||||
|
||||
func TestWinPerfCountersConfigGet6Integration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
instances := []string{"------"}
|
||||
counters := []string{"Context Switches/sec"}
|
||||
perfObjects := []perfObject{{
|
||||
ObjectName: "System",
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: "test",
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}}
|
||||
|
||||
m := WinPerfCounters{
|
||||
PrintValid: false,
|
||||
Object: perfObjects,
|
||||
MaxBufferSize: defaultMaxBufferSize,
|
||||
Log: testutil.Logger{},
|
||||
queryCreator: &performanceQueryCreatorImpl{},
|
||||
}
|
||||
|
||||
require.NoError(t, m.parseConfig())
|
||||
|
||||
_, ok := m.hostCounters["localhost"]
|
||||
require.True(t, ok)
|
||||
}
|
||||
|
||||
func TestWinPerfCountersConfigGet7Integration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
instances := []string{"_Total"}
|
||||
counters := []string{"% Processor Time", "% Processor TimeERROR", "% Idle Time"}
|
||||
perfObjects := []perfObject{{
|
||||
ObjectName: "Processor Information",
|
||||
Counters: counters,
|
||||
Instances: instances,
|
||||
Measurement: "test",
|
||||
}}
|
||||
|
||||
m := WinPerfCounters{
|
||||
PrintValid: false,
|
||||
Object: perfObjects,
|
||||
MaxBufferSize: defaultMaxBufferSize,
|
||||
Log: testutil.Logger{},
|
||||
queryCreator: &performanceQueryCreatorImpl{},
|
||||
}
|
||||
|
||||
require.NoError(t, m.parseConfig())
|
||||
|
||||
hostCounters, ok := m.hostCounters["localhost"]
|
||||
require.True(t, ok)
|
||||
require.Len(t, hostCounters.counters, 2, "There should be exactly two results returned from the counterPath")
|
||||
}
|
||||
|
||||
func TestWinPerfCountersConfigError1Integration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
instances := []string{"_Total"}
|
||||
counters := []string{"% Processor Time"}
|
||||
perfObjects := []perfObject{{
|
||||
ObjectName: "Processor InformationERROR",
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: "test",
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}}
|
||||
|
||||
m := WinPerfCounters{
|
||||
PrintValid: false,
|
||||
Object: perfObjects,
|
||||
MaxBufferSize: defaultMaxBufferSize,
|
||||
Log: testutil.Logger{},
|
||||
queryCreator: &performanceQueryCreatorImpl{},
|
||||
}
|
||||
|
||||
require.Error(t, m.parseConfig())
|
||||
}
|
||||
|
||||
func TestWinPerfCountersConfigError2Integration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
instances := []string{"SuperERROR"}
|
||||
counters := []string{"% C1 Time"}
|
||||
perfObjects := []perfObject{{
|
||||
ObjectName: "Processor",
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: "test",
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}}
|
||||
|
||||
m := WinPerfCounters{
|
||||
PrintValid: false,
|
||||
Object: perfObjects,
|
||||
MaxBufferSize: defaultMaxBufferSize,
|
||||
Log: testutil.Logger{},
|
||||
queryCreator: &performanceQueryCreatorImpl{},
|
||||
}
|
||||
|
||||
require.NoError(t, m.parseConfig())
|
||||
var acc testutil.Accumulator
|
||||
require.Error(t, m.Gather(&acc))
|
||||
}
|
||||
|
||||
func TestWinPerfCountersConfigError3Integration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
instances := []string{"_Total"}
|
||||
counters := []string{"% Processor TimeERROR"}
|
||||
perfObjects := []perfObject{{
|
||||
ObjectName: "Processor Information",
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: "test",
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}}
|
||||
|
||||
m := WinPerfCounters{
|
||||
PrintValid: false,
|
||||
Object: perfObjects,
|
||||
MaxBufferSize: defaultMaxBufferSize,
|
||||
Log: testutil.Logger{},
|
||||
queryCreator: &performanceQueryCreatorImpl{},
|
||||
}
|
||||
|
||||
require.Error(t, m.parseConfig())
|
||||
}
|
||||
|
||||
func TestWinPerfCountersCollect1Integration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
instances := []string{"_Total"}
|
||||
counters := []string{"Parking Status"}
|
||||
perfObjects := []perfObject{{
|
||||
ObjectName: "Processor Information",
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: "test",
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}}
|
||||
|
||||
m := WinPerfCounters{
|
||||
PrintValid: false,
|
||||
Object: perfObjects,
|
||||
MaxBufferSize: defaultMaxBufferSize,
|
||||
Log: testutil.Logger{},
|
||||
queryCreator: &performanceQueryCreatorImpl{},
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
require.NoError(t, m.Gather(&acc))
|
||||
|
||||
time.Sleep(2000 * time.Millisecond)
|
||||
require.NoError(t, m.Gather(&acc))
|
||||
require.Len(t, acc.Metrics, 2)
|
||||
|
||||
for _, metric := range acc.Metrics {
|
||||
_, ok := metric.Fields["Parking_Status"]
|
||||
require.True(t, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWinPerfCountersCollect2Integration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
instances := []string{"_Total", "0,0"}
|
||||
counters := []string{"Performance Limit Flags"}
|
||||
perfObjects := []perfObject{{
|
||||
ObjectName: "Processor Information",
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: "test",
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}}
|
||||
|
||||
m := WinPerfCounters{
|
||||
PrintValid: false,
|
||||
UsePerfCounterTime: true,
|
||||
Object: perfObjects,
|
||||
UseWildcardsExpansion: true,
|
||||
MaxBufferSize: defaultMaxBufferSize,
|
||||
Log: testutil.Logger{},
|
||||
queryCreator: &performanceQueryCreatorImpl{},
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
require.NoError(t, m.Gather(&acc))
|
||||
|
||||
time.Sleep(2000 * time.Millisecond)
|
||||
require.NoError(t, m.Gather(&acc))
|
||||
|
||||
require.Len(t, acc.Metrics, 4)
|
||||
|
||||
for _, metric := range acc.Metrics {
|
||||
_, ok := metric.Fields["Performance_Limit_Flags"]
|
||||
require.True(t, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWinPerfCountersCollectRawIntegration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
instances := []string{"*"}
|
||||
counters := []string{"% Idle Time"}
|
||||
perfObjects := []perfObject{{
|
||||
ObjectName: "Processor",
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: "test",
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
UseRawValues: true,
|
||||
}}
|
||||
|
||||
m := WinPerfCounters{
|
||||
PrintValid: false,
|
||||
Object: perfObjects,
|
||||
UseWildcardsExpansion: true,
|
||||
MaxBufferSize: defaultMaxBufferSize,
|
||||
Log: testutil.Logger{},
|
||||
queryCreator: &performanceQueryCreatorImpl{},
|
||||
}
|
||||
var acc testutil.Accumulator
|
||||
require.NoError(t, m.Gather(&acc))
|
||||
|
||||
time.Sleep(2000 * time.Millisecond)
|
||||
require.NoError(t, m.Gather(&acc))
|
||||
require.Greater(t, len(acc.Metrics), 1)
|
||||
|
||||
expectedCounter := "Percent_Idle_Time_Raw"
|
||||
for _, metric := range acc.Metrics {
|
||||
val, ok := metric.Fields[expectedCounter]
|
||||
require.True(t, ok, "Expected presence of %s field", expectedCounter)
|
||||
valInt64, ok := val.(int64)
|
||||
require.Truef(t, ok, "Expected int64, got %T", val)
|
||||
require.Positivef(t, valInt64, "Value not positive for metric %#v", metric)
|
||||
}
|
||||
|
||||
// Test *Array way
|
||||
m = WinPerfCounters{
|
||||
PrintValid: false,
|
||||
Object: perfObjects,
|
||||
UseWildcardsExpansion: false,
|
||||
MaxBufferSize: defaultMaxBufferSize,
|
||||
Log: testutil.Logger{},
|
||||
queryCreator: &performanceQueryCreatorImpl{},
|
||||
}
|
||||
var acc2 testutil.Accumulator
|
||||
require.NoError(t, m.Gather(&acc))
|
||||
|
||||
time.Sleep(2000 * time.Millisecond)
|
||||
require.NoError(t, m.Gather(&acc2))
|
||||
require.Greater(t, len(acc2.Metrics), 1)
|
||||
|
||||
for _, metric := range acc2.Metrics {
|
||||
val, ok := metric.Fields[expectedCounter]
|
||||
require.True(t, ok, "Expected presence of %s field", expectedCounter)
|
||||
valInt64, ok := val.(int64)
|
||||
require.Truef(t, ok, "Expected int64, got %T", val)
|
||||
require.Positivef(t, valInt64, "Value not positive for metric %#v", metric)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
//go:build !windows
|
||||
|
||||
package win_perf_counters
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
type WinPerfCounters struct {
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
}
|
||||
|
||||
func (*WinPerfCounters) SampleConfig() string { return sampleConfig }
|
||||
|
||||
func (w *WinPerfCounters) Init() error {
|
||||
w.Log.Warn("Current platform is not supported")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*WinPerfCounters) Gather(telegraf.Accumulator) error { return nil }
|
||||
|
||||
func init() {
|
||||
inputs.Add("win_perf_counters", func() telegraf.Input {
|
||||
return &WinPerfCounters{}
|
||||
})
|
||||
}
|
2100
plugins/inputs/win_perf_counters/win_perf_counters_test.go
Normal file
2100
plugins/inputs/win_perf_counters/win_perf_counters_test.go
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue