fix: Add setting to win_perf_counters input to ignore localization (#10101)

This commit is contained in:
reimda 2021-11-23 15:11:00 -07:00 committed by GitHub
parent 2b43934174
commit 6518745153
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 233 additions and 39 deletions

View File

@ -20,6 +20,10 @@ as counters used when performance monitoring
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.
### Plugin wide
Plugin wide entries are underneath `[[inputs.win_perf_counters]]`.
@ -33,19 +37,39 @@ Example:
#### UseWildcardsExpansion
If `UseWildcardsExpansion` is set to true, wildcards can be used in the
instance name and the counter name. When using localized Windows, counters
will be also be localized. Instance indexes will also be returned in the
instance name.
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.
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.
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
@ -63,7 +87,7 @@ Example:
#### PreVistaSupport
_Deprecated. Necessary features on Windows Vista and newer are checked dynamically_
(Deprecated. 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.
@ -74,7 +98,7 @@ Example for Windows Server 2003, this would be set to true:
#### UsePerfCounterTime
Bool, if set to `true` will request a timestamp along with the PerfCounter data.
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
@ -86,6 +110,7 @@ Example:
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,
@ -94,14 +119,16 @@ 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**
(Required)
ObjectName is the Object to query for, like Processor, DirectoryServices, LogicalDisk or similar.
Example: `ObjectName = "LogicalDisk"`
#### Instances
**Required**
(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.
@ -121,7 +148,8 @@ Here only one option is valid if you want data back,
and that is to specify `Instances = ["------"]`.
#### Counters
**Required**
(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.
@ -133,7 +161,8 @@ This must be specified for every counter you want the results of, or use
is set to `true`.
#### Measurement
*Optional*
(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.
@ -144,7 +173,8 @@ separately from Processor results.
Example: `Measurement = "win_disk"``
#### IncludeTotal
*Optional*
(Optional)
This key is optional. It is a simple bool.
If it is not set to true or included it is treated as false.
@ -154,7 +184,8 @@ like `_Total`, `0,_Total` and so on where applicable
(Processor Information is one example).
#### WarnOnMissing
*Optional*
(Optional)
This key is optional. It is a simple bool.
If it is not set to true or included it is treated as false.
@ -163,7 +194,8 @@ It will print out any ObjectName/Instance/Counter combinations
asked for that do not match. Useful when debugging new configurations.
#### FailOnMissing
*Internal*
(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.
@ -173,6 +205,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid.
## Examples
### Generic Queries
```toml
[[inputs.win_perf_counters]]
[[inputs.win_perf_counters.object]]
@ -217,6 +250,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid.
```
### Active Directory Domain Controller
```toml
[[inputs.win_perf_counters]]
[inputs.win_perf_counters.tags]
@ -245,6 +279,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid.
```
### DFS Namespace + Domain Controllers
```toml
[[inputs.win_perf_counters]]
[[inputs.win_perf_counters.object]]
@ -258,6 +293,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid.
```
### DFS Replication + Domain Controllers
```toml
[[inputs.win_perf_counters]]
[[inputs.win_perf_counters.object]]
@ -271,6 +307,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid.
```
### DNS Server + Domain Controllers
```toml
[[inputs.win_perf_counters]]
[[inputs.win_perf_counters.object]]
@ -282,6 +319,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid.
```
### IIS / ASP.NET
```toml
[[inputs.win_perf_counters]]
[[inputs.win_perf_counters.object]]
@ -326,6 +364,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid.
```
### Process
```toml
[[inputs.win_perf_counters]]
[[inputs.win_perf_counters.object]]
@ -338,6 +377,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid.
```
### .NET Monitoring
```toml
[[inputs.win_perf_counters]]
[[inputs.win_perf_counters.object]]
@ -402,6 +442,7 @@ your performance counters.
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:
```
lodctr /r
```
```batchfile
lodctr /r
```

View File

@ -28,6 +28,11 @@ var sampleConfig = `
# 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"
@ -141,11 +146,12 @@ var sampleConfig = `
type Win_PerfCounters struct {
PrintValid bool
//deprecated: determined dynamically
PreVistaSupport bool
UsePerfCounterTime bool
Object []perfobject
CountersRefreshInterval config.Duration
UseWildcardsExpansion bool
PreVistaSupport bool
UsePerfCounterTime bool
Object []perfobject
CountersRefreshInterval config.Duration
UseWildcardsExpansion bool
LocalizeWildcardsExpansion bool
Log telegraf.Logger
@ -247,6 +253,7 @@ func (m *Win_PerfCounters) SampleConfig() string {
//objectName string, counter string, instance string, measurement string, include_total bool
func (m *Win_PerfCounters) AddItem(counterPath string, objectName string, instance string, counterName string, measurement string, includeTotal bool) error {
origCounterPath := counterPath
var err error
var counterHandle PDH_HCOUNTER
if !m.query.IsVistaOrNewer() {
@ -273,21 +280,55 @@ func (m *Win_PerfCounters) AddItem(counterPath string, objectName string, instan
return err
}
origObjectName, _, origCounterName, err := extractCounterInfoFromCounterPath(origCounterPath)
if err != nil {
return err
}
for _, counterPath := range counters {
var err error
counterHandle, err := m.query.AddCounterToQuery(counterPath)
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(origObjectName, newInstance, origCounterName)
counterHandle, err = m.query.AddEnglishCounterToQuery(counterPath)
newItem = &counter{
counterPath,
origObjectName, origCounterName,
instance, measurement,
includeTotal, counterHandle,
}
} else {
counterHandle, err = m.query.AddCounterToQuery(counterPath)
newItem = &counter{
counterPath,
objectName, counterName,
instance, measurement,
includeTotal, counterHandle,
}
}
if instance == "_Total" && origInstance == "*" && !includeTotal {
continue
}
newItem := &counter{counterPath, objectName, counterName, instance, measurement,
includeTotal, counterHandle}
m.counters = append(m.counters, newItem)
if m.PrintValid {
@ -306,6 +347,16 @@ func (m *Win_PerfCounters) AddItem(counterPath string, objectName string, instan
return nil
}
const emptyInstance = "------"
func formatPath(objectname string, instance string, counter string) string {
if instance == emptyInstance {
return "\\" + objectname + "\\" + counter
} else {
return "\\" + objectname + "(" + instance + ")\\" + counter
}
}
func (m *Win_PerfCounters) ParseConfig() error {
var counterPath string
@ -315,11 +366,7 @@ func (m *Win_PerfCounters) ParseConfig() error {
for _, instance := range PerfObject.Instances {
objectname := PerfObject.ObjectName
if instance == "------" {
counterPath = "\\" + objectname + "\\" + counter
} else {
counterPath = "\\" + objectname + "(" + instance + ")\\" + counter
}
counterPath = formatPath(objectname, instance, counter)
err := m.AddItem(counterPath, objectname, instance, counter, PerfObject.Measurement, PerfObject.IncludeTotal)
@ -447,7 +494,7 @@ func shouldIncludeMetric(metric *counter, cValue CounterValue) bool {
// Catch if we set it to total or some form of it
return true
}
if metric.instance == "------" {
if metric.instance == emptyInstance {
return true
}
return false
@ -476,8 +523,43 @@ func isKnownCounterDataError(err error) bool {
return false
}
func (m *Win_PerfCounters) Init() error {
if m.UseWildcardsExpansion && !m.LocalizeWildcardsExpansion {
// Counters must not have wildcards with this option
found := false
wildcards := []string{"*", "?"}
for _, object := range m.Object {
for _, wildcard := range wildcards {
if strings.Contains(object.ObjectName, wildcard) {
found = true
m.Log.Errorf("object: %s, contains wildcard %s", object.ObjectName, wildcard)
}
}
for _, counter := range object.Counters {
for _, wildcard := range wildcards {
if strings.Contains(counter, wildcard) {
found = true
m.Log.Errorf("object: %s, counter: %s contains wildcard %s", object.ObjectName, counter, wildcard)
}
}
}
}
if found {
return fmt.Errorf("wildcards can't be used with LocalizeWildcardsExpansion=false")
}
}
return nil
}
func init() {
inputs.Add("win_perf_counters", func() telegraf.Input {
return &Win_PerfCounters{query: &PerformanceQueryImpl{}, CountersRefreshInterval: config.Duration(time.Second * 60)}
return &Win_PerfCounters{
query: &PerformanceQueryImpl{},
CountersRefreshInterval: config.Duration(time.Second * 60),
LocalizeWildcardsExpansion: true,
}
})
}

View File

@ -452,7 +452,12 @@ func TestWinPerfcountersConfigError1Integration(t *testing.T) {
perfobjects[0] = PerfObject
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}}
m := Win_PerfCounters{
PrintValid: false,
Object: perfobjects,
query: &PerformanceQueryImpl{},
Log: testutil.Logger{},
}
m.query.Open()
err := m.ParseConfig()
@ -486,7 +491,12 @@ func TestWinPerfcountersConfigError2Integration(t *testing.T) {
perfobjects[0] = PerfObject
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}}
m := Win_PerfCounters{
PrintValid: false,
Object: perfobjects,
query: &PerformanceQueryImpl{},
Log: testutil.Logger{},
}
m.query.Open()
err := m.ParseConfig()
@ -522,7 +532,12 @@ func TestWinPerfcountersConfigError3Integration(t *testing.T) {
perfobjects[0] = PerfObject
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}}
m := Win_PerfCounters{
PrintValid: false,
Object: perfobjects,
query: &PerformanceQueryImpl{},
Log: testutil.Logger{},
}
m.query.Open()
err := m.ParseConfig()
@ -557,7 +572,12 @@ func TestWinPerfcountersCollect1Integration(t *testing.T) {
perfobjects[0] = PerfObject
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}}
m := Win_PerfCounters{
PrintValid: false,
Object: perfobjects,
query: &PerformanceQueryImpl{},
Log: testutil.Logger{},
}
var acc testutil.Accumulator
err := m.Gather(&acc)
require.NoError(t, err)
@ -603,7 +623,14 @@ func TestWinPerfcountersCollect2Integration(t *testing.T) {
perfobjects[0] = PerfObject
m := Win_PerfCounters{PrintValid: false, UsePerfCounterTime: true, Object: perfobjects, query: &PerformanceQueryImpl{}, UseWildcardsExpansion: true}
m := Win_PerfCounters{
PrintValid: false,
UsePerfCounterTime: true,
Object: perfobjects,
query: &PerformanceQueryImpl{},
UseWildcardsExpansion: true,
Log: testutil.Logger{},
}
var acc testutil.Accumulator
err := m.Gather(&acc)
require.NoError(t, err)

View File

@ -1024,3 +1024,47 @@ func TestUTF16ToStringArray(t *testing.T) {
czechStrings := UTF16ToStringArray(unicodeStringListWithCzechChars)
require.Equal(t, czechStrings, stringArrayWithCzechChars, "Not equal czech arrays")
}
func TestNoWildcards(t *testing.T) {
m := Win_PerfCounters{
Object: createPerfObject("measurement", "object", []string{"instance"}, []string{"counter*"}, false, false),
UseWildcardsExpansion: true,
LocalizeWildcardsExpansion: false,
Log: testutil.Logger{},
}
require.Error(t, m.Init())
m = Win_PerfCounters{
Object: createPerfObject("measurement", "object?", []string{"instance"}, []string{"counter"}, false, false),
UseWildcardsExpansion: true,
LocalizeWildcardsExpansion: false,
Log: testutil.Logger{},
}
require.Error(t, m.Init())
}
func TestLocalizeWildcardsExpansion(t *testing.T) {
// this test is valid only on localized windows
if testing.Short() {
t.Skip("Skipping long taking test in short mode")
}
const counter = "% Processor Time"
m := Win_PerfCounters{
query: &PerformanceQueryImpl{},
CountersRefreshInterval: config.Duration(time.Second * 60),
Object: createPerfObject("measurement", "Processor Information",
[]string{"_Total"}, []string{counter}, false, false),
LocalizeWildcardsExpansion: false,
UseWildcardsExpansion: true,
Log: testutil.Logger{},
}
require.NoError(t, m.Init())
var acc testutil.Accumulator
require.NoError(t, m.Gather(&acc))
require.Len(t, acc.Metrics, 1)
//running on localized windows with UseWildcardsExpansion and
//with LocalizeWildcardsExpansion, this will be localized. Using LocalizeWildcardsExpansion=false it will
//be English.
require.Contains(t, acc.Metrics[0].Fields, sanitizedChars.Replace(counter))
}