diff --git a/plugins/inputs/win_perf_counters/README.md b/plugins/inputs/win_perf_counters/README.md index de45386a7..dcc15d638 100644 --- a/plugins/inputs/win_perf_counters/README.md +++ b/plugins/inputs/win_perf_counters/README.md @@ -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 +``` diff --git a/plugins/inputs/win_perf_counters/win_perf_counters.go b/plugins/inputs/win_perf_counters/win_perf_counters.go index 3a74e34a5..a126db4ea 100644 --- a/plugins/inputs/win_perf_counters/win_perf_counters.go +++ b/plugins/inputs/win_perf_counters/win_perf_counters.go @@ -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, + } }) } diff --git a/plugins/inputs/win_perf_counters/win_perf_counters_integration_test.go b/plugins/inputs/win_perf_counters/win_perf_counters_integration_test.go index c7ceec815..634833793 100644 --- a/plugins/inputs/win_perf_counters/win_perf_counters_integration_test.go +++ b/plugins/inputs/win_perf_counters/win_perf_counters_integration_test.go @@ -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) diff --git a/plugins/inputs/win_perf_counters/win_perf_counters_test.go b/plugins/inputs/win_perf_counters/win_perf_counters_test.go index 998423e79..5519e3d37 100644 --- a/plugins/inputs/win_perf_counters/win_perf_counters_test.go +++ b/plugins/inputs/win_perf_counters/win_perf_counters_test.go @@ -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)) +}