From eea5a50d5cc016faeabd62720bb778f6b0404ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 9 Dec 2024 19:14:26 +0100 Subject: [PATCH] performancecounter: support yaml documents and tolerate collector errors (#1809) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- .run/all.run.xml | 4 +- cmd/windows_exporter/main.go | 4 +- config.yaml | 9 +- docs/collector.performancecounter.md | 47 +++- internal/collector/mssql/mssql.go | 2 +- .../performancecounter/performancecounter.go | 225 +++++++++++++----- .../performancecounter_test_test.go | 87 ++++++- .../collector/performancecounter/types.go | 1 + internal/types/errors.go | 4 +- pkg/collector/collect.go | 3 +- pkg/collector/collection.go | 17 +- pkg/collector/map.go | 3 + tools/e2e-output.txt | 4 + tools/end-to-end-test.ps1 | 2 +- 14 files changed, 323 insertions(+), 89 deletions(-) diff --git a/.run/all.run.xml b/.run/all.run.xml index 65ec0a71d..5b5035b88 100644 --- a/.run/all.run.xml +++ b/.run/all.run.xml @@ -2,7 +2,7 @@ - + @@ -10,4 +10,4 @@ - \ No newline at end of file + diff --git a/cmd/windows_exporter/main.go b/cmd/windows_exporter/main.go index f28d3a20c..63a47d10c 100644 --- a/cmd/windows_exporter/main.go +++ b/cmd/windows_exporter/main.go @@ -207,9 +207,11 @@ func run() int { // Initialize collectors before loading if err = collectors.Build(logger); err != nil { for _, err := range utils.SplitError(err) { - logger.Warn("couldn't initialize collector", + logger.Error("couldn't initialize collector", slog.Any("err", err), ) + + return 1 } } diff --git a/config.yaml b/config.yaml index 07222f730..d26e5f893 100644 --- a/config.yaml +++ b/config.yaml @@ -1,9 +1,16 @@ # example configuration file for windows_exporter collectors: - enabled: cpu,cpu_info,exchange,iis,logical_disk,logon,memory,net,os,process,remote_fx,service,system,tcp,time,terminal_services,textfile + enabled: cpu,cpu_info,exchange,iis,logical_disk,logon,memory,net,os,performancecounter,process,remote_fx,service,system,tcp,time,terminal_services,textfile collector: service: include: "windows_exporter" + performancecounter: + objects: |- + - name: memory + object: "Memory" + counters: + - name: "Cache Faults/sec" + type: "counter" # optional log: level: warn diff --git a/docs/collector.performancecounter.md b/docs/collector.performancecounter.md index 778b6f0bd..16c906f02 100644 --- a/docs/collector.performancecounter.md +++ b/docs/collector.performancecounter.md @@ -13,9 +13,28 @@ The performancecounter collector exposes any configured metric. ### `--collector.performancecounter.objects` -Objects is a list of objects to collect metrics from. The value takes the form of a JSON array of strings. YAML is also supported. +Objects is a list of objects to collect metrics from. The value takes the form of a JSON array of strings. +YAML is supported. -The collector supports only english named counter. Localized counter-names are not supported. +The collector supports only English-named counter. Localized counter-names aren’t supported. + +> [!CAUTION] +> If you are using a configuration file, the value must be kept as a string. +> +> Use a `|-` to keep the value as a string. + +#### Example + +```yaml +collector: + performancecounter: + objects: |- + - name: memory + object: "Memory" + counters: + - name: "Cache Faults/sec" + type: "counter" # optional +``` #### Schema @@ -25,7 +44,8 @@ YAML: Click to expand YAML schema ```yaml -- object: "Processor Information" +- name: cpu # free text name + object: "Processor Information" # Performance counter object name instances: ["*"] instance_label: "core" counters: @@ -37,7 +57,8 @@ YAML: metric: windows_performancecounter_processor_information_processor_time # optional labels: state: idle -- object: "Memory" +- name: memory + object: "Memory" counters: - name: "Cache Faults/sec" type: "counter" # optional @@ -51,6 +72,7 @@ YAML: ```json [ { + "name": "cpu", "object": "Processor Information", "instances": [ "*" @@ -74,6 +96,7 @@ YAML: ] }, { + "name": "memory", "object": "Memory", "counters": [ { @@ -86,6 +109,11 @@ YAML: ``` +#### name + +The name is used to identify the object in the logs and metrics. +Must unique across all objects. + #### object ObjectName is the Object to query for, like Processor, DirectoryServices, LogicalDisk or similar. @@ -186,6 +214,17 @@ windows_performancecounter_processor_information_processor_time{core="0,8",state windows_performancecounter_processor_information_processor_time{core="0,9",state="active"} 1.0059484375e+11 windows_performancecounter_processor_information_processor_time{core="0,9",state="idle"} 10059.484375 ``` +> [!NOTE] +> If you are using a configuration file, the value must be keep as string. + +Example: + +```yaml +collector: + performancecounter: + objects: | +``` + ## Metrics diff --git a/internal/collector/mssql/mssql.go b/internal/collector/mssql/mssql.go index db87764db..6d851b356 100644 --- a/internal/collector/mssql/mssql.go +++ b/internal/collector/mssql/mssql.go @@ -387,7 +387,7 @@ func (c *Collector) collect( slog.Any("err", err), ) } else { - c.logger.Debug(fmt.Sprintf("mssql class collector %s for instance %s succeeded after %s.", collector, sqlInstance, duration)) + c.logger.Debug(fmt.Sprintf("mssql class collector %s for instance %s succeeded after %s", collector, sqlInstance, duration)) } ch <- prometheus.MustNewConstMetric( diff --git a/internal/collector/performancecounter/performancecounter.go b/internal/collector/performancecounter/performancecounter.go index aed4b0c22..99452a07b 100644 --- a/internal/collector/performancecounter/performancecounter.go +++ b/internal/collector/performancecounter/performancecounter.go @@ -16,16 +16,19 @@ package performancecounter import ( - "encoding/json" + "errors" "fmt" "log/slog" + "slices" "strings" + "time" "github.com/alecthomas/kingpin/v2" "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "gopkg.in/yaml.v3" ) const Name = "performancecounter" @@ -44,6 +47,14 @@ type Collector struct { config Config logger *slog.Logger + + objects []Object + + metricNameReplacer *strings.Replacer + + // meta + subCollectorScrapeDurationDesc *prometheus.Desc + subCollectorScrapeSuccessDesc *prometheus.Desc } func New(config *Config) *Collector { @@ -79,7 +90,7 @@ func NewWithFlags(app *kingpin.Application) *Collector { return nil } - if err := json.Unmarshal([]byte(objects), &c.config.Objects); err != nil { + if err := yaml.Unmarshal([]byte(objects), &c.config.Objects); err != nil { return fmt.Errorf("failed to parse objects: %w", err) } @@ -104,107 +115,199 @@ func (c *Collector) Close() error { func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error { c.logger = logger.With(slog.String("collector", Name)) + c.metricNameReplacer = strings.NewReplacer( + ".", "", + "%", "", + "/", "_", + " ", "_", + "-", "_", + ) + + c.objects = make([]Object, 0, len(c.config.Objects)) + names := make([]string, 0, len(c.config.Objects)) + + var errs []error + for i, object := range c.config.Objects { + if object.Name == "" { + return errors.New("object name is required") + } + + if object.Object == "" { + errs = append(errs, fmt.Errorf("object %s: object is required", object.Name)) + + continue + } + + if slices.Contains(names, object.Name) { + errs = append(errs, fmt.Errorf("object %s: name is duplicated", object.Name)) + + continue + } + + names = append(names, object.Name) counters := make([]string, 0, len(object.Counters)) - for j, counter := range object.Counters { - counters = append(counters, counter.Name) + for j, counter := range object.Counters { if counter.Metric == "" { - c.config.Objects[i].Counters[j].Metric = sanitizeMetricName(fmt.Sprintf("%s_%s_%s_%s", types.Namespace, Name, object.Object, counter.Name)) + c.config.Objects[i].Counters[j].Metric = c.sanitizeMetricName( + fmt.Sprintf("%s_%s_%s_%s", types.Namespace, Name, object.Object, counter.Name), + ) + } + + if counter.Name == "" { + errs = append(errs, errors.New("counter name is required")) + c.config.Objects = slices.Delete(c.config.Objects, i, 1) + + continue + } + + if slices.Contains(counters, counter.Name) { + errs = append(errs, fmt.Errorf("counter name %s is duplicated", counter.Name)) + + continue } + + counters = append(counters, counter.Name) } collector, err := perfdata.NewCollector(object.Object, object.Instances, counters) if err != nil { - return fmt.Errorf("failed to create v2 collector: %w", err) + errs = append(errs, fmt.Errorf("failed collector for %s: %w", object.Name, err)) } if object.InstanceLabel == "" { - c.config.Objects[i].InstanceLabel = "instance" + object.InstanceLabel = "instance" } - c.config.Objects[i].collector = collector + object.collector = collector + + c.objects = append(c.objects, object) } - return nil + c.subCollectorScrapeDurationDesc = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "collector_duration_seconds"), + "windows_exporter: Duration of an performancecounter child collection.", + []string{"collector"}, + nil, + ) + c.subCollectorScrapeSuccessDesc = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "collector_success"), + "windows_exporter: Whether a performancecounter child collector was successful.", + []string{"collector"}, + nil, + ) + + return errors.Join(errs...) } // Collect sends the metric values for each metric // to the provided prometheus Metric channel. func (c *Collector) Collect(ch chan<- prometheus.Metric) error { - for _, perfDataObject := range c.config.Objects { - collectedPerfData, err := perfDataObject.collector.Collect() + var errs []error + + for _, perfDataObject := range c.objects { + startTime := time.Now() + err := c.collectObject(ch, perfDataObject) + duration := time.Since(startTime) + success := 1.0 + if err != nil { - return fmt.Errorf("failed to collect data: %w", err) + errs = append(errs, fmt.Errorf("failed to collect object %s: %w", perfDataObject.Name, err)) + success = 0.0 + + c.logger.Debug(fmt.Sprintf("performancecounter collector %s failed after %s", perfDataObject.Name, duration), + slog.Any("err", err), + ) + } else { + c.logger.Debug(fmt.Sprintf("performancecounter collector %s succeeded after %s", perfDataObject.Name, duration)) } - for collectedInstance, collectedInstanceCounters := range collectedPerfData { - for _, counter := range perfDataObject.Counters { - collectedCounterValue, ok := collectedInstanceCounters[counter.Name] - if !ok { - c.logger.Warn(fmt.Sprintf("counter %s not found in collected data", counter.Name)) + ch <- prometheus.MustNewConstMetric( + c.subCollectorScrapeSuccessDesc, + prometheus.GaugeValue, + success, + perfDataObject.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.subCollectorScrapeDurationDesc, + prometheus.GaugeValue, + duration.Seconds(), + perfDataObject.Name, + ) + } + + return errors.Join(errs...) +} + +func (c *Collector) collectObject(ch chan<- prometheus.Metric, perfDataObject Object) error { + collectedPerfData, err := perfDataObject.collector.Collect() + if err != nil { + return fmt.Errorf("failed to collect data: %w", err) + } + + var errs []error - continue - } + for collectedInstance, collectedInstanceCounters := range collectedPerfData { + for _, counter := range perfDataObject.Counters { + collectedCounterValue, ok := collectedInstanceCounters[counter.Name] + if !ok { + errs = append(errs, fmt.Errorf("counter %s not found in collected data", counter.Name)) - labels := make(prometheus.Labels, len(counter.Labels)+1) - if collectedInstance != perfdata.InstanceEmpty { - labels[perfDataObject.InstanceLabel] = collectedInstance - } + continue + } + + labels := make(prometheus.Labels, len(counter.Labels)+1) + + if collectedInstance != perfdata.InstanceEmpty { + labels[perfDataObject.InstanceLabel] = collectedInstance + } - for key, value := range counter.Labels { - labels[key] = value - } + for key, value := range counter.Labels { + labels[key] = value + } - var metricType prometheus.ValueType + var metricType prometheus.ValueType - switch counter.Type { - case "counter": - metricType = prometheus.CounterValue - case "gauge": - metricType = prometheus.GaugeValue - default: - metricType = collectedCounterValue.Type - } + switch counter.Type { + case "counter": + metricType = prometheus.CounterValue + case "gauge": + metricType = prometheus.GaugeValue + default: + metricType = collectedCounterValue.Type + } + ch <- prometheus.MustNewConstMetric( + prometheus.NewDesc( + counter.Metric, + "windows_exporter: custom Performance Counter metric", + nil, + labels, + ), + metricType, + collectedCounterValue.FirstValue, + ) + + if collectedCounterValue.SecondValue != 0 { ch <- prometheus.MustNewConstMetric( prometheus.NewDesc( - counter.Metric, + counter.Metric+"_second", "windows_exporter: custom Performance Counter metric", nil, labels, ), metricType, - collectedCounterValue.FirstValue, + collectedCounterValue.SecondValue, ) - - if collectedCounterValue.SecondValue != 0 { - ch <- prometheus.MustNewConstMetric( - prometheus.NewDesc( - counter.Metric+"_second", - "windows_exporter: custom Performance Counter metric", - nil, - labels, - ), - metricType, - collectedCounterValue.SecondValue, - ) - } } } } - return nil + return errors.Join(errs...) } -func sanitizeMetricName(name string) string { - replacer := strings.NewReplacer( - ".", "", - "%", "", - "/", "_", - " ", "_", - "-", "_", - ) - - return strings.Trim(replacer.Replace(strings.ToLower(name)), "_") +func (c *Collector) sanitizeMetricName(name string) string { + return strings.Trim(c.metricNameReplacer.Replace(strings.ToLower(name)), "_") } diff --git a/internal/collector/performancecounter/performancecounter_test_test.go b/internal/collector/performancecounter/performancecounter_test_test.go index 6424156d3..6d482c4bb 100644 --- a/internal/collector/performancecounter/performancecounter_test_test.go +++ b/internal/collector/performancecounter/performancecounter_test_test.go @@ -49,38 +49,91 @@ func TestCollector(t *testing.T) { t.Parallel() for _, tc := range []struct { + name string object string instances []string instanceLabel string + buildErr string counters []performancecounter.Counter expectedMetrics *regexp.Regexp }{ { - object: "Memory", - instances: nil, - counters: []performancecounter.Counter{{Name: "Available Bytes", Type: "gauge"}}, - expectedMetrics: regexp.MustCompile(`^# HELP windows_performancecounter_memory_available_bytes windows_exporter: custom Performance Counter metric\S*\s*# TYPE windows_performancecounter_memory_available_bytes gauge\s*windows_performancecounter_memory_available_bytes \d`), + name: "memory", + object: "Memory", + instances: nil, + buildErr: "", + counters: []performancecounter.Counter{{Name: "Available Bytes", Type: "gauge"}}, + expectedMetrics: regexp.MustCompile(`^# HELP windows_performancecounter_collector_duration_seconds windows_exporter: Duration of an performancecounter child collection. +# TYPE windows_performancecounter_collector_duration_seconds gauge +windows_performancecounter_collector_duration_seconds\{collector="memory"} [0-9.e+-]+ +# HELP windows_performancecounter_collector_success windows_exporter: Whether a performancecounter child collector was successful. +# TYPE windows_performancecounter_collector_success gauge +windows_performancecounter_collector_success\{collector="memory"} 1 +# HELP windows_performancecounter_memory_available_bytes windows_exporter: custom Performance Counter metric +# TYPE windows_performancecounter_memory_available_bytes gauge +windows_performancecounter_memory_available_bytes [0-9.e+-]+`), + }, + { + name: "process", + object: "Process", + instances: []string{"*"}, + buildErr: "", + counters: []performancecounter.Counter{{Name: "Thread Count", Type: "counter"}}, + expectedMetrics: regexp.MustCompile(`^# HELP windows_performancecounter_collector_duration_seconds windows_exporter: Duration of an performancecounter child collection. +# TYPE windows_performancecounter_collector_duration_seconds gauge +windows_performancecounter_collector_duration_seconds\{collector="process"} [0-9.e+-]+ +# HELP windows_performancecounter_collector_success windows_exporter: Whether a performancecounter child collector was successful. +# TYPE windows_performancecounter_collector_success gauge +windows_performancecounter_collector_success\{collector="process"} 1 +# HELP windows_performancecounter_process_thread_count windows_exporter: custom Performance Counter metric +# TYPE windows_performancecounter_process_thread_count counter +windows_performancecounter_process_thread_count\{instance=".+"} [0-9.e+-]+ +.*`), }, { - object: "Process", - instances: []string{"*"}, - counters: []performancecounter.Counter{{Name: "Thread Count", Type: "counter"}}, - expectedMetrics: regexp.MustCompile(`^# HELP windows_performancecounter_process_thread_count windows_exporter: custom Performance Counter metric\S*\s*# TYPE windows_performancecounter_process_thread_count counter\s*windows_performancecounter_process_thread_count\{instance=".+"} \d`), + name: "processor_information", + object: "Processor Information", + instances: []string{"*"}, + instanceLabel: "core", + buildErr: "", + counters: []performancecounter.Counter{{Name: "% Processor Time", Metric: "windows_performancecounter_processor_information_processor_time", Labels: map[string]string{"state": "active"}}, {Name: "% Idle Time", Metric: "windows_performancecounter_processor_information_processor_time", Labels: map[string]string{"state": "idle"}}}, + expectedMetrics: regexp.MustCompile(`^# HELP windows_performancecounter_collector_duration_seconds windows_exporter: Duration of an performancecounter child collection. +# TYPE windows_performancecounter_collector_duration_seconds gauge +windows_performancecounter_collector_duration_seconds\{collector="processor_information"} [0-9.e+-]+ +# HELP windows_performancecounter_collector_success windows_exporter: Whether a performancecounter child collector was successful. +# TYPE windows_performancecounter_collector_success gauge +windows_performancecounter_collector_success\{collector="processor_information"} 1 +# HELP windows_performancecounter_processor_information_processor_time windows_exporter: custom Performance Counter metric +# TYPE windows_performancecounter_processor_information_processor_time counter +windows_performancecounter_processor_information_processor_time\{core="0,0",state="active"} [0-9.e+-]+ +windows_performancecounter_processor_information_processor_time\{core="0,0",state="idle"} [0-9.e+-]+ +.*`), }, { + name: "", object: "Processor Information", - instances: []string{"*"}, - instanceLabel: "core", - counters: []performancecounter.Counter{{Name: "% Processor Time", Metric: "windows_performancecounter_processor_information_processor_time", Labels: map[string]string{"state": "active"}}, {Name: "% Idle Time", Metric: "windows_performancecounter_processor_information_processor_time", Labels: map[string]string{"state": "idle"}}}, - expectedMetrics: regexp.MustCompile(`^# HELP windows_performancecounter_processor_information_processor_time windows_exporter: custom Performance Counter metric\s+# TYPE windows_performancecounter_processor_information_processor_time counter\s+windows_performancecounter_processor_information_processor_time\{core="0,0",state="active"} [0-9.e+]+\s+windows_performancecounter_processor_information_processor_time\{core="0,0",state="idle"} [0-9.e+]+`), + instances: nil, + instanceLabel: "", + buildErr: "object name is required", + counters: nil, + expectedMetrics: nil, + }, + { + name: "double_counter", + object: "Memory", + instances: nil, + buildErr: "counter name Available Bytes is duplicated", + counters: []performancecounter.Counter{{Name: "Available Bytes", Type: "gauge"}, {Name: "Available Bytes", Type: "gauge"}}, + expectedMetrics: nil, }, } { - t.Run(tc.object, func(t *testing.T) { + t.Run(tc.name, func(t *testing.T) { t.Parallel() perfDataCollector := performancecounter.New(&performancecounter.Config{ Objects: []performancecounter.Object{ { + Name: tc.name, Object: tc.object, Instances: tc.instances, InstanceLabel: tc.instanceLabel, @@ -91,6 +144,13 @@ func TestCollector(t *testing.T) { logger := slog.New(slog.NewTextHandler(io.Discard, nil)) err := perfDataCollector.Build(logger, nil) + + if tc.buildErr != "" { + require.ErrorContains(t, err, tc.buildErr) + + return + } + require.NoError(t, err) registry := prometheus.NewRegistry() @@ -101,6 +161,7 @@ func TestCollector(t *testing.T) { got := rw.Body.String() assert.NotEmpty(t, got) + require.NotEmpty(t, tc.expectedMetrics) assert.Regexp(t, tc.expectedMetrics, got) }) } diff --git a/internal/collector/performancecounter/types.go b/internal/collector/performancecounter/types.go index 3fe91c0a3..a01d56918 100644 --- a/internal/collector/performancecounter/types.go +++ b/internal/collector/performancecounter/types.go @@ -18,6 +18,7 @@ package performancecounter import "github.com/prometheus-community/windows_exporter/internal/perfdata" type Object struct { + Name string `json:"name" yaml:"name"` Object string `json:"object" yaml:"object"` Instances []string `json:"instances" yaml:"instances"` Counters []Counter `json:"counters" yaml:"counters"` diff --git a/internal/types/errors.go b/internal/types/errors.go index da4f4c1c1..3694d7cc9 100644 --- a/internal/types/errors.go +++ b/internal/types/errors.go @@ -13,9 +13,7 @@ package types -import ( - "errors" -) +import "errors" var ( ErrCollectorNotInitialized = errors.New("collector not initialized") diff --git a/pkg/collector/collect.go b/pkg/collector/collect.go index c684dc691..233bd27a7 100644 --- a/pkg/collector/collect.go +++ b/pkg/collector/collect.go @@ -206,8 +206,9 @@ func (c *Collection) collectCollector(ch chan<- prometheus.Metric, logger *slog. if err != nil && !errors.Is(err, perfdata.ErrNoData) && !errors.Is(err, types.ErrNoData) { loggerFn := logger.Warn + if errors.Is(err, perfdata.ErrPerformanceCounterNotInitialized) || errors.Is(err, mi.MI_RESULT_INVALID_NAMESPACE) { - loggerFn = logger.Debug + err = fmt.Errorf("%w. Check application logs from initialization pharse for more information", err) } loggerFn(fmt.Sprintf("collector %s failed after %s, resulting in %d metrics", name, duration, numMetrics), diff --git a/pkg/collector/collection.go b/pkg/collector/collection.go index de71a2d68..c79f36386 100644 --- a/pkg/collector/collection.go +++ b/pkg/collector/collection.go @@ -74,6 +74,7 @@ import ( "github.com/prometheus-community/windows_exporter/internal/collector/update" "github.com/prometheus-community/windows_exporter/internal/collector/vmware" "github.com/prometheus-community/windows_exporter/internal/mi" + "github.com/prometheus-community/windows_exporter/internal/perfdata" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" ) @@ -196,6 +197,8 @@ func (c *Collection) Enable(enabledCollectors []string) error { } // Build To be called by the exporter for collector initialization. +// Instead, fail fast, it will try to build all collectors and return all errors. +// errors are joined with errors.Join. func (c *Collection) Build(logger *slog.Logger) error { c.startTime = gotime.Now() @@ -208,7 +211,6 @@ func (c *Collection) Build(logger *slog.Logger) error { wg.Add(len(c.collectors)) errCh := make(chan error, len(c.collectors)) - errs := make([]error, 0, len(c.collectors)) for _, collector := range c.collectors { go func() { @@ -224,7 +226,20 @@ func (c *Collection) Build(logger *slog.Logger) error { close(errCh) + errs := make([]error, 0, len(c.collectors)) + for err := range errCh { + if errors.Is(err, perfdata.ErrNoData) || + errors.Is(err, perfdata.NewPdhError(perfdata.PdhCstatusNoObject)) || + errors.Is(err, perfdata.NewPdhError(perfdata.PdhCstatusNoCounter)) || + errors.Is(err, mi.MI_RESULT_INVALID_NAMESPACE) { + logger.Warn("couldn't initialize collector", + slog.Any("err", err), + ) + + continue + } + errs = append(errs, err) } diff --git a/pkg/collector/map.go b/pkg/collector/map.go index ec45e7751..957aebfe9 100644 --- a/pkg/collector/map.go +++ b/pkg/collector/map.go @@ -128,6 +128,9 @@ var BuildersWithFlags = map[string]BuilderWithFlags[Collector]{ vmware.Name: NewBuilderWithFlags(vmware.NewWithFlags), } +// Available returns a sorted list of available collectors. +// +//goland:noinspection GoUnusedExportedFunction func Available() []string { return slices.Sorted(maps.Keys(BuildersWithFlags)) } diff --git a/tools/e2e-output.txt b/tools/e2e-output.txt index 680ed5e96..c03cfbd02 100644 --- a/tools/e2e-output.txt +++ b/tools/e2e-output.txt @@ -319,6 +319,10 @@ windows_exporter_collector_timeout{collector="udp"} 0 # TYPE windows_pagefile_free_bytes gauge # HELP windows_pagefile_limit_bytes Number of bytes that can be stored in the operating system paging files. 0 (zero) indicates that there are no paging files # TYPE windows_pagefile_limit_bytes gauge +# HELP windows_performancecounter_collector_duration_seconds windows_exporter: Duration of an performancecounter child collection. +# TYPE windows_performancecounter_collector_duration_seconds gauge +# HELP windows_performancecounter_collector_success windows_exporter: Whether a performancecounter child collector was successful. +# TYPE windows_performancecounter_collector_success gauge # HELP windows_performancecounter_memory_cache_faults_sec windows_exporter: custom Performance Counter metric # TYPE windows_performancecounter_memory_cache_faults_sec counter # HELP windows_performancecounter_processor_information_processor_time windows_exporter: custom Performance Counter metric diff --git a/tools/end-to-end-test.ps1 b/tools/end-to-end-test.ps1 index 85e5bc9cb..49de5b5a8 100644 --- a/tools/end-to-end-test.ps1 +++ b/tools/end-to-end-test.ps1 @@ -26,7 +26,7 @@ $exporter_proc = Start-Process ` -PassThru ` -FilePath ..\windows_exporter.exe ` -ArgumentList "--log.level=debug","--web.disable-exporter-metrics","--collectors.enabled=[defaults],cpu_info,textfile,process,pagefile,performancecounter,scheduled_task,tcp,udp,time,system,service,logical_disk,printer,os,net,memory,logon,cache","--collector.process.include=explorer.exe","--collector.scheduled_task.include=.*GAEvents","--collector.service.include=Themes","--collector.textfile.directories=$($textfile_dir)",@" ---collector.performancecounter.objects="[{\"object\":\"Processor Information\",\"instances\":[\"*\"],\"instance_label\":\"core\",\"counters\":[{\"name\":\"% Processor Time\",\"metric\":\"windows_performancecounter_processor_information_processor_time\",\"labels\":{\"state\":\"active\"}},{\"name\":\"% Idle Time\",\"metric\":\"windows_performancecounter_processor_information_processor_time\",\"labels\":{\"state\":\"idle\"}}]},{\"object\":\"Memory\",\"counters\":[{\"name\":\"Cache Faults/sec\",\"type\":\"counter\"}]}]" +--collector.performancecounter.objects="[{\"name\":\"cpu\",\"object\":\"Processor Information\",\"instances\":[\"*\"],\"instance_label\":\"core\",\"counters\":[{\"name\":\"% Processor Time\",\"metric\":\"windows_performancecounter_processor_information_processor_time\",\"labels\":{\"state\":\"active\"}},{\"name\":\"% Idle Time\",\"metric\":\"windows_performancecounter_processor_information_processor_time\",\"labels\":{\"state\":\"idle\"}}]},{\"name\":\"memory\",\"object\":\"Memory\",\"counters\":[{\"name\":\"Cache Faults/sec\",\"type\":\"counter\"}]}]" "@ ` -WindowStyle Hidden ` -RedirectStandardOutput "$($temp_dir)/windows_exporter.log" `