diff --git a/CHANGELOG.md b/CHANGELOG.md index f8cef8831..177968763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,7 @@ consistent with the behavior of `collection_jitter`. - [#1213](https://github.com/influxdata/telegraf/issues/1213): Add inactive & active memory to mem plugin. - [#1543](https://github.com/influxdata/telegraf/pull/1543): Official Windows service. - [#1414](https://github.com/influxdata/telegraf/pull/1414): Forking sensors command to remove C package dependency. +- [FIXME](https://github.com/influxdata/telegraf/issues/FIXME): Adds `wpc` input plugin as an alternative to `win_perf_counters`. ### Bugfixes diff --git a/plugins/inputs/all/all.go b/plugins/inputs/all/all.go index dacbff644..2d8cf5d5a 100644 --- a/plugins/inputs/all/all.go +++ b/plugins/inputs/all/all.go @@ -74,6 +74,7 @@ import ( _ "github.com/influxdata/telegraf/plugins/inputs/varnish" _ "github.com/influxdata/telegraf/plugins/inputs/webhooks" _ "github.com/influxdata/telegraf/plugins/inputs/win_perf_counters" + _ "github.com/influxdata/telegraf/plugins/inputs/wpc" _ "github.com/influxdata/telegraf/plugins/inputs/zfs" _ "github.com/influxdata/telegraf/plugins/inputs/zookeeper" ) diff --git a/plugins/inputs/wpc/README.md b/plugins/inputs/wpc/README.md new file mode 100644 index 000000000..21e42fa11 --- /dev/null +++ b/plugins/inputs/wpc/README.md @@ -0,0 +1,133 @@ +# wpc Input Plugin + +This plugin gathers stats from [Windows Performance Counters](https://msdn.microsoft.com/en-us/library/windows/desktop/aa373083(v=vs.85).aspx) + +### Configuration: + +```toml + # A plugin to collect stats from Windows Performance Counters + [[inputs.wpc]] + ## If the system being polled for data does not have a particular Counter at startup + ## of the Telegraf agent, it will not be gathered. + # Prints all matching performance counters (useful for debugging) + # PrintValid = false + + [[inputs.wpc.template]] + # Processor usage, alternative to native. + Counters = [ + # Use double-backslashes to work around a TOML parsing issue. + [ "usage_idle", "\\Processor(*)\\%% Idle Time" ], + [ "usage_user", "\\Processor(*)\\%% User Time" ], + [ "usage_system", "\\Processor(*)\\%% Processor Time" ] + ] + Measurement = "win_cpu" + # Print out when the performance counter is missing from object, counter or instance. + # WarnOnMissing = false +``` + +## `wpc` vs `win_perf_counters` + +The `win_perf_counters` plugin generates tags and fields using native Windows names. This can make it difficult to compare common measurements across heterogenous environments because Windows names tend towards complexity. For example, on Windows the performance counter "\\Processor(*)\\%% User Time" is equivalent to the Linux metric "cpu.usage_user" - good luck displaying both series on the same plot in Grafana. + +Additionally, `win_perf_counters` can generate an large number of series in an InfluxDB database due to the inclusion of the Windows Performance Counter Object Name (e.g. Processor, Processor Information, Memory, etc) in the tag list. According to the [Hardware Sizing Guidelines](https://docs.influxdata.com/influxdb/v0.13/guides/hardware_sizing/#when-do-i-need-more-ram), series cardinality strongly affects the amount of RAM required by the InfluxDB server. Therefore, there is a risk that heavily instrumented Windows machines can unduly impact the provisioning requirements of the InfluxDB server simply due to the use of `win_perf_counters`. + +The `wpc` plugin mitigates these two potential issues by making Performance Counter queries field names explicit, and by transparently regrouping fully-qualified Performance Counter queries by instance to minimize the number of points generated. + +## An IIS / ASP.NET example + +The templates below could be one way of instrumenting an IIS/ASP.NET web server. + +``` + [[inputs.wpc.template]] + Counters = [ + [ "get_rate", "\\Web Service(*)\\Get Requests/sec" ], + [ "post_rate", "\\Web Service(*)\\Post Requests/sec" ], + [ "conn_rate", "\\Web Service(*)\\Connection Attempts/sec" ], + [ "conn", "\\Web Service(*)\\Current Connections" ], + [ "isapi_rate", "\\Web Service(*)\\ISAPI Extension Requests/sec" ], + # These queries are remapped to equivalent HAProxy field names. + [ "qcur", "\\HTTP Service Request Queues(*)\\CurrentQueueSize" ], + [ "dreq", "\\HTTP Service Request Queues(*)\\RejectedRequests" ], + ] + Measurement = "iis_websvc" + ## Example output + # iis_websvc,instance=HelloWorld get_rate=4i,post_rate=10i,conn_rate=1i,conn=100i,isapi_rate=0i,qcur=0i,dreq=0i 1462765437090957980 + + [[inputs.wpc.template]] + Counters = [ + [ "restart", "\\ASP.NET\\Application Restarts" ], + [ "wait_time", "\\ASP.NET\\Request Wait Time" ], + [ "requests", "\\ASP.NET\\Requests Current" ], + [ "waiting", "\\ASP.NET\\Requests Queued" ], + [ "rejected", "\\ASP.NET\\Requests Rejected" ], + [ "cache_tot", "\\ASP.NET Applications(__Total__)\\Cache Total Entries" ], + [ "cache_hit", "\\ASP.NET Applications(__Total__)\\Cache Total Hit Ratio" ], + [ "error_rate", "\\ASP.NET Applications(__Total__)\\Errors Total/Sec" ], + [ "req_rate", "\\ASP.NET Applications(__Total__)\\Requests/Sec" ], + [ "user_hit_rate", "\\Web Service Cache\\URI Cache Hits %" ], + [ "system_hit_rate", "\\Web Service Cache\\Kernel: URI Cache Hits %" ], + [ "file_hit_rate", "\\Web Service Cache\\File Cache Hits %" ], + ] + Measurement = "iis" + ## Example output + # iis,instance= restart=0i,wait_time=0i,requests=0i,waiting=0i,rejected=3i,user_hit_rate=4.99,system_hit_rate=3.34,file_hit_rate=9.3 1462765437090957980 + # iis,instance=__Total__ cache_tot=100000i,cache_hit=55.555555,error_rate=3.324,req_rate=0 +``` + + +## Exploring Windows Performance Counters with `typeperf` + +Windows ships with a command-line utility named `typeperf` that can be used to query, explore, and sample the same fully-qualified Performance Counter queries that the `wpc` plugin expects. + +```Dos +C:\> typeperf /? + +Microsoft r TypePerf.exe (6.3.9600.17415) + +Typeperf writes performance data to the command window or to a log file. To +stop Typeperf, press CTRL+C. + +Usage: +typeperf { | -cf | -q [object] + | -qx [object] } [options] + +Parameters: + Performance counters to monitor. + +Options: + -? Displays context sensitive help. + -f Output file format. Default is CSV. + -cf File containing performance counters to + monitor, one per line. + -si <[[hh:]mm:]ss> Time between samples. Default is 1 second. + -o Path of output file or SQL database. Default + is STDOUT. + -q [object] List installed counters (no instances). To + list counters for one object, include the + object name, such as Processor. + -qx [object] List installed counters with instances. To + list counters for one object, include the + object name, such as Processor. + -sc Number of samples to collect. Default is to + sample until CTRL+C. + -config Settings file containing command options. + -s Server to monitor if no server is specified + in the counter path. + -y Answer yes to all questions without prompting. + +Note: + Counter is the full name of a performance counter in + "\\\()\" format, + such as "\\Server1\Processor(0)\% User Time". + +Examples: + typeperf "\Processor(_Total)\% Processor Time" + typeperf -cf counters.txt -si 5 -sc 50 -f TSV -o domain2.tsv + typeperf -qx PhysicalDisk -o counters.txt +``` + + +## Future Features + +- Add the ability to alias Instance values and/or the ability to specify sets of equivalent instances (e.g. [ "", "_Total", "__Total__" ]). + diff --git a/plugins/inputs/wpc/wpc.go b/plugins/inputs/wpc/wpc.go new file mode 100644 index 000000000..dc1fdfd9c --- /dev/null +++ b/plugins/inputs/wpc/wpc.go @@ -0,0 +1,316 @@ +// +build windows + +package wpc + +import ( + "errors" + "fmt" + "strings" + "unsafe" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/inputs" + lxn "github.com/lxn/win" +) + +var sampleConfig string = ` + ## A plugin to collect stats from Windows Performance Counters. + ## If the system being polled for data does not have a particular Counter at startup + ## of the Telegraf agent, it will not be gathered. + # Prints all matching performance counters (useful for debugging) + # PrintValid = false + + [[inputs.wpc.template]] + # Processor usage, alternative to native. + Counters = [ + [ "usage_idle", "\\Processor(*)\\%% Idle Time" ], + [ "usage_user", "\\Processor(*)\\%% User Time" ], + [ "usage_system", "\\Processor(*)\\%% Processor Time" ] + ] + Measurement = "win_cpu" + # Print out when the performance counter is missing from object, counter or instance. + # WarnOnMissing = false + + + [[inputs.wpc.template]] + # Disk times and queues + Counters = [ + [ "usage_idle", "\\LogicalDisk(*)\\%% Idle Time" ], + [ "usage_used", "\\LogicalDisk(*)\\%% Disk Time" ], + [ "usage_read", "\\LogicalDisk(*)\\%% Disk Read Time" ], + [ "usage_write", "\\LogicalDisk(*)\\%% Disk Write Time" ], + [ "usage_user", "\\LogicalDisk(*)\\%% User Time" ], + [ "qcur", "\\LogicalDisk(*)\\Current Disk Queue Length" ] + ] + Measurement = "win_diskio" + # Print out when the performance counter is missing from object, counter or instance. + # WarnOnMissing = false + + [[inputs.wpc.template]] + # System and memory details + Counters = [ + [ "cs_rate", "\\System\\Context Switches/sec" ], + [ "syscall_rate", "\\System\\System Calls/sec" ], + [ "mem_available", "\\Memory\\Available Bytes" ] + ] + Measurement = "win_system" + # Print out when the performance counter is missing from object, counter or instance. + # WarnOnMissing = false +` + +type WindowsPerformanceCounter struct { + PrintValid bool + TestName string + PreVistaSupport bool + Template []template +} + +type template struct { + Counters [][]string + Measurement string + WarnOnMissing bool + FailOnMissing bool +} + +type task struct { + measurement string + fields map[string]*counter +} + +type counter struct { + query string + handle lxn.PDH_HQUERY + counterHandle lxn.PDH_HCOUNTER + current map[string]float32 +} + +// Globals +var ( + gConfigParsed bool + + // Parsed configuration ends up here after it has been validated + gTaskList []*task + + // Counter cache to avoid gathering the same counter more than once per Gather + gCounterCache = make(map[string]*counter) + + // Various error messages + errBadConfig error = errors.New("inputs.wpc.series contains invalid configuration") + errObjectNotExist error = errors.New("Performance object does not exist") + errCounterNotExist error = errors.New("Counter in Performance object does not exist") + errInstanceNotExist error = errors.New("Instance in Performance object does not exist") + errBadQuery error = errors.New("Invalid query for Performance Counters") + + // Used to cleanup instance names + sanitizedChars = strings.NewReplacer("/sec", "_persec", "/Sec", "_persec", " ", "_", "%", "Percent", `\`, "", ",", "_") +) + +func (m *WindowsPerformanceCounter) Description() string { + return "Input plugin to query Performance Counters on Windows operating systems" +} + +func (m *WindowsPerformanceCounter) SampleConfig() string { + return sampleConfig +} + +func (m *WindowsPerformanceCounter) Gather(acc telegraf.Accumulator) error { + // We only need to parse the config during the init, it uses the global variable after. + if gConfigParsed == false { + err := m.parseConfig() + gConfigParsed = true + if err != nil { + return err + } + } + + // Sample counters + for _, c := range gCounterCache { + if ok := c.queryPerformanceCounter(); !ok { + continue + } + } + + type grouping struct { + fields map[string]interface{} + tags map[string]string + } + + for _, t := range gTaskList { + groups := make(map[string]*grouping) + + // Regroup samples by (measurement, instance) to minimize points generated. + for field, c := range t.fields { + for instance, f32 := range c.current { + g, ok := groups[instance] + if !ok { + g = &grouping{ + tags: make(map[string]string), + fields: make(map[string]interface{})} + g.tags["instance"] = sanitizedChars.Replace(instance) + groups[instance] = g + } + + g.fields[field] = f32 + } + } + + for _, g := range groups { + acc.AddFields(t.measurement, g.fields, g.tags) + } + } + + return nil +} + +// Samples (instance, value) tuples from the performance counter +func (c *counter) queryPerformanceCounter() (ok bool) { + if ret := lxn.PdhCollectQueryData(c.handle); ret != lxn.ERROR_SUCCESS { + return false + } + + var bufSize uint32 + var bufCount uint32 + var size uint32 = uint32(unsafe.Sizeof(lxn.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE{})) + var emptyBuf [1]lxn.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE // need at least 1 addressable null ptr. + + // uses null ptr here according to MSDN. + if ret := lxn.PdhGetFormattedCounterArrayDouble(c.counterHandle, &bufSize, &bufCount, &emptyBuf[0]); ret != lxn.PDH_MORE_DATA || bufCount == 0 { + return false + } + + coll := make(map[string]float32) + data := make([]lxn.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size) + + _ = lxn.PdhGetFormattedCounterArrayDouble(c.counterHandle, &bufSize, &bufCount, &data[0]) + for i := 0; i < int(bufCount); i++ { + res := data[i] + instance := lxn.UTF16PtrToString(res.SzName) + value := float32(res.FmtValue.DoubleValue) + coll[instance] = value + } + + c.current = coll + return true +} + +func (m *WindowsPerformanceCounter) validatePerformanceCounterPath(query string, onMissingWarn, onMissingFail bool) (ok bool, err error) { + const ( + lxn_PDH_CSTATUS_NO_OBJECT uint32 = 3221228472 + lxn_PDH_CSTATUS_NO_COUNTER uint32 = 3221228473 + lxn_PDH_CSTATUS_NO_INSTANCE uint32 = 2147485649 + ) + + exists := lxn.PdhValidatePath(query) + if exists == lxn.ERROR_SUCCESS { + if m.PrintValid { + fmt.Printf("Valid: %s\n", query) + } + + return true, nil + } else if !onMissingWarn && !onMissingFail { + return false, nil + } + + switch exists { + case lxn_PDH_CSTATUS_NO_OBJECT: + if onMissingFail { + return false, errObjectNotExist + } + + fmt.Printf("Performance Object does not exist in query: %s\n", query) + break + + case lxn_PDH_CSTATUS_NO_COUNTER: + if onMissingFail { + return false, errCounterNotExist + } + + fmt.Printf("Counter does not exist in query: %s\n", query) + break + + case lxn_PDH_CSTATUS_NO_INSTANCE: + if onMissingFail { + return false, errInstanceNotExist + } + + fmt.Printf("Instance does not exist in query: %s\n", query) + break + + default: + fmt.Printf("Invalid result: %v, query: %s\n", exists, query) + if onMissingFail { + return false, errBadQuery + } + break + } + + return false, nil +} + +func (m *WindowsPerformanceCounter) openPerformanceCounter(query string) *counter { + var handle lxn.PDH_HQUERY + var counterHandle lxn.PDH_HCOUNTER + + ret := lxn.PdhOpenQuery(0, 0, &handle) + if m.PreVistaSupport { + ret = lxn.PdhAddCounter(handle, query, 0, &counterHandle) + } else { + ret = lxn.PdhAddEnglishCounter(handle, query, 0, &counterHandle) + } + _ = ret + + return &counter{query, handle, counterHandle, nil} +} + +// Populates the global counter cache and task list. +func (m *WindowsPerformanceCounter) parseConfig() error { + if len(m.Template) == 0 { + err := errors.New("Nothing to do!") + return err + } + + for _, tmpl := range m.Template { + t := &task{ + measurement: tmpl.Measurement, + fields: make(map[string]*counter), + } + + for _, pair := range tmpl.Counters { + if len(pair) != 2 { + return errBadConfig + } + + field := pair[0] + query := pair[1] + + if _, ok := gCounterCache[query]; !ok { + if ok, err := m.validatePerformanceCounterPath(query, tmpl.WarnOnMissing, tmpl.FailOnMissing); !ok && err != nil { + return err + } else if !ok { + continue + } + + gCounterCache[query] = m.openPerformanceCounter(query) + } + + t.fields[field] = gCounterCache[query] + } + + gTaskList = append(gTaskList, t) + } + + return nil +} + +// func (m *WindowsPerformanceCounter) cleanup(metrics *itemList) { +// // Cleanup + +// for _, metric := range metrics.items { +// ret := lxn.PdhCloseQuery(metric.handle) +// _ = ret +// } +// } + +func init() { + inputs.Add("wpc", func() telegraf.Input { return &WindowsPerformanceCounter{} }) +} diff --git a/plugins/inputs/wpc/wpc_notwindows.go b/plugins/inputs/wpc/wpc_notwindows.go new file mode 100644 index 000000000..e376de447 --- /dev/null +++ b/plugins/inputs/wpc/wpc_notwindows.go @@ -0,0 +1,3 @@ +// +build !windows + +package wpc diff --git a/plugins/inputs/wpc/wpc_test.go b/plugins/inputs/wpc/wpc_test.go new file mode 100644 index 000000000..7ee610cba --- /dev/null +++ b/plugins/inputs/wpc/wpc_test.go @@ -0,0 +1,396 @@ +// +build windows + +package wpc + +import ( + "errors" + "testing" + "time" + + "github.com/influxdata/telegraf/testutil" + "github.com/stretchr/testify/require" +) + +func TestWPCConfigGet1(t *testing.T) { + var templates = make([]template, 1) + + counters := [][]string{ + []string{"foo", "\\Processor Information(_Total)\\%% Processor Time"}, + } + + var measurement string = "test" + var warnonmissing bool = false + var failonmissing bool = true + + tmpl := template{ + Counters: counters, + Measurement: measurement, + WarnOnMissing: warnonmissing, + FailOnMissing: failonmissing, + } + + templates[0] = tmpl + + m := WindowsPerformanceCounter{PrintValid: false, TestName: "ConfigGet1", Template: templates} + + err := m.parseConfig() + require.NoError(t, err) +} + +func TestWPCConfigGet2(t *testing.T) { + var templates = make([]template, 1) + + counters := [][]string{ + []string{"foo", "\\Processor Information(_Total)\\%% Processor Time"}, + } + + var measurement string = "test" + var warnonmissing bool = false + var failonmissing bool = true + + tmpl := template{ + Counters: counters, + Measurement: measurement, + WarnOnMissing: warnonmissing, + FailOnMissing: failonmissing, + } + + templates[0] = tmpl + + m := WindowsPerformanceCounter{PrintValid: false, TestName: "ConfigGet2", Template: templates} + + err := m.parseConfig() + require.NoError(t, err) + + require.Equal(t, 1, len(gTaskList), "Wrong number of tasks defined.") + require.Equal(t, 1, len(gCounterCache), "Wrong number of counters opened.") + require.Equal(t, 1, len(gTaskList[0].fields), "Wrong number of field mappings defined.") + require.Equal(t, "test", gTaskList[0].measurement, "Wrong measurement saved.") +} + +func TestWPCConfigGet3(t *testing.T) { + var templates = make([]template, 1) + + counters := [][]string{ + []string{"foo", "\\Processor Information(_Total)\\%% Processor Time"}, + []string{"bar", "\\Processor Information(_Total)\\%% Processor Time"}, + } + + var measurement string = "test" + var warnonmissing bool = false + var failonmissing bool = true + + tmpl := template{ + Counters: counters, + Measurement: measurement, + WarnOnMissing: warnonmissing, + FailOnMissing: failonmissing, + } + + templates[0] = tmpl + + m := WindowsPerformanceCounter{PrintValid: false, TestName: "ConfigGet3", Template: templates} + + err := m.parseConfig() + require.NoError(t, err) + + require.Equal(t, 1, len(gTaskList), "Wrong number of tasks defined.") + require.Equal(t, 1, len(gCounterCache), "Wrong number of counters opened.") + require.Equal(t, 2, len(gTaskList[0].fields), "Wrong number of field mappings defined.") + require.Equal(t, "test", gTaskList[0].measurement, "Wrong measurement saved.") +} + +func TestWPCConfigGet4(t *testing.T) { + var templates = make([]template, 1) + + counters := [][]string{ + []string{"foo", "\\Processor Information(_Total)\\%% Processor Time"}, + []string{"bar", "\\System\\Context Switches/sec"}, + } + + var measurement string = "test" + var warnonmissing bool = false + var failonmissing bool = true + + tmpl := template{ + Counters: counters, + Measurement: measurement, + WarnOnMissing: warnonmissing, + FailOnMissing: failonmissing, + } + + templates[0] = tmpl + + m := WindowsPerformanceCounter{PrintValid: false, TestName: "ConfigGet4", Template: templates} + + err := m.parseConfig() + require.NoError(t, err) + + require.Equal(t, 1, len(gTaskList), "Wrong number of tasks defined.") + require.Equal(t, 2, len(gCounterCache), "Wrong number of counters opened.") + require.Equal(t, 2, len(gTaskList[0].fields), "Wrong number of field mappings defined.") + require.Equal(t, "test", gTaskList[0].measurement, "Wrong measurement saved.") +} + +func TestWPCConfigGet5(t *testing.T) { + var templates = make([]template, 1) + + counters := [][]string{ + []string{"foo", "\\Processor Information(*)\\%% Processor Time"}, + } + + var measurement string = "test" + var warnonmissing bool = false + var failonmissing bool = true + + tmpl := template{ + Counters: counters, + Measurement: measurement, + WarnOnMissing: warnonmissing, + FailOnMissing: failonmissing, + } + + templates[0] = tmpl + + m := WindowsPerformanceCounter{PrintValid: false, TestName: "ConfigGet5", Template: templates} + + err := m.parseConfig() + require.NoError(t, err) + + require.Equal(t, 1, len(gTaskList), "Wrong number of tasks defined.") + require.Equal(t, 1, len(gCounterCache), "Wrong number of counters opened.") + require.Equal(t, 1, len(gTaskList[0].fields), "Wrong number of field mappings defined.") + require.Equal(t, "test", gTaskList[0].measurement, "Wrong measurement saved.") +} + +func TestWPCConfigGet6(t *testing.T) { + var templates = make([]template, 1) + + counters := [][]string{ + []string{"foo", "\\Processor Information(*)\\%% Processor TimeERROR"}, + []string{"bar", "\\Processor Information(*)\\%% Idle Time"}, + } + + var measurement string = "test" + var warnonmissing bool = false + var failonmissing bool = false + + tmpl := template{ + Counters: counters, + Measurement: measurement, + WarnOnMissing: warnonmissing, + FailOnMissing: failonmissing, + } + + templates[0] = tmpl + + m := WindowsPerformanceCounter{PrintValid: false, TestName: "ConfigGet6", Template: templates} + + err := m.parseConfig() + require.NoError(t, err) + + require.Equal(t, 1, len(gTaskList), "Wrong number of tasks defined.") + require.Equal(t, 1, len(gCounterCache), "Wrong number of counters opened.") + require.Equal(t, 1, len(gTaskList[0].fields), "Wrong number of field mappings defined.") + require.Equal(t, "test", gTaskList[0].measurement, "Wrong measurement saved.") +} + +func TestWPCConfigError1(t *testing.T) { + var templates = make([]template, 1) + + counters := [][]string{ + []string{"foo", "\\Processor InformationERROR(*)\\%% Processor Time"}, + } + + var measurement string = "test" + var warnonmissing bool = false + var failonmissing bool = false + + tmpl := template{ + Counters: counters, + Measurement: measurement, + WarnOnMissing: warnonmissing, + FailOnMissing: failonmissing, + } + + templates[0] = tmpl + + m := WindowsPerformanceCounter{PrintValid: false, TestName: "ConfigError1", Template: templates} + + err := m.parseConfig() + require.Error(t, err) +} + +func TestWPCConfigError2(t *testing.T) { + var templates = make([]template, 1) + + counters := [][]string{ + []string{"foo", "\\Processor(SuperERROR)\\%% C1 Time"}, + } + + var measurement string = "test" + var warnonmissing bool = false + var failonmissing bool = false + + tmpl := template{ + Counters: counters, + Measurement: measurement, + WarnOnMissing: warnonmissing, + FailOnMissing: failonmissing, + } + + templates[0] = tmpl + + m := WindowsPerformanceCounter{PrintValid: false, TestName: "ConfigError2", Template: templates} + + err := m.parseConfig() + require.Error(t, err) +} + +func TestWPCConfigError3(t *testing.T) { + var templates = make([]template, 1) + + counters := [][]string{ + []string{"foo", "\\Processor Information(*)\\%% Processor TimeERROR"}, + } + + var measurement string = "test" + var warnonmissing bool = false + var failonmissing bool = true + + tmpl := template{ + Counters: counters, + Measurement: measurement, + WarnOnMissing: warnonmissing, + FailOnMissing: failonmissing, + } + + templates[0] = tmpl + + m := WindowsPerformanceCounter{PrintValid: false, TestName: "ConfigError3", Template: templates} + + err := m.parseConfig() + require.Error(t, err) +} + +func TestWPCCollect1(t *testing.T) { + var templates = make([]template, 1) + + counters := [][]string{ + []string{"park", "\\Processor Information(_Total)\\Parking Status"}, + } + + var measurement string = "test" + var warnonmissing bool = false + var failonmissing bool = true + + tmpl := template{ + Counters: counters, + Measurement: measurement, + WarnOnMissing: warnonmissing, + FailOnMissing: failonmissing, + } + + templates[0] = tmpl + + m := WindowsPerformanceCounter{PrintValid: false, TestName: "Collect1", Template: templates} + var acc testutil.Accumulator + err := m.Gather(&acc) + require.NoError(t, err) + + time.Sleep(2000 * time.Millisecond) + err = m.Gather(&acc) + + tags := map[string]string{ + "instance": "_Total", + } + fields := map[string]interface{}{ + "park": float32(0), + } + acc.AssertContainsTaggedFields(t, measurement, fields, tags) +} + +func TestWPCCollect2(t *testing.T) { + var templates = make([]template, 1) + + counters := [][]string{ + []string{"park", "\\Processor Information(_Total)\\Parking Status"}, + []string{"plim", "\\Processor Information(_Total)\\Performance Limit Flags"}, + } + + var measurement string = "test" + var warnonmissing bool = false + var failonmissing bool = true + + tmpl := template{ + Counters: counters, + Measurement: measurement, + WarnOnMissing: warnonmissing, + FailOnMissing: failonmissing, + } + + templates[0] = tmpl + + m := WindowsPerformanceCounter{PrintValid: false, TestName: "Collect2", Template: templates} + var acc testutil.Accumulator + err := m.Gather(&acc) + require.NoError(t, err) + + time.Sleep(2000 * time.Millisecond) + err = m.Gather(&acc) + + tags := map[string]string{ + "instance": "_Total", + } + fields := map[string]interface{}{ + "park": float32(0), + "plim": float32(0), + } + acc.AssertContainsTaggedFields(t, measurement, fields, tags) +} + +func TestWPCCollect3(t *testing.T) { + var templates = make([]template, 1) + + counters := [][]string{ + []string{"park", "\\Processor Information(_Total)\\Parking Status"}, + []string{"sys_cs_rate", "\\System\\Context Switches/sec"}, + } + + var measurement string = "test" + var warnonmissing bool = false + var failonmissing bool = true + + tmpl := template{ + Counters: counters, + Measurement: measurement, + WarnOnMissing: warnonmissing, + FailOnMissing: failonmissing, + } + + templates[0] = tmpl + + m := WindowsPerformanceCounter{PrintValid: false, TestName: "Collect3", Template: templates} + var acc testutil.Accumulator + err := m.Gather(&acc) + require.NoError(t, err) + + time.Sleep(2000 * time.Millisecond) + err = m.Gather(&acc) + + tags := map[string]string{ + "instance": "_Total", + } + fields := map[string]interface{}{ + "park": float32(0), + } + acc.AssertContainsTaggedFields(t, measurement, fields, tags) + + tags = map[string]string{ + "instance": "", + } + fields = map[string]interface{}{ + "sys_cs_rate": float32(0), + } + acc.AssertContainsTaggedFields(t, measurement, fields, tags) +}