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/performancecounter/performancecounter.go b/internal/collector/performancecounter/performancecounter.go index 7882f5229..26a593d21 100644 --- a/internal/collector/performancecounter/performancecounter.go +++ b/internal/collector/performancecounter/performancecounter.go @@ -19,6 +19,7 @@ import ( "errors" "fmt" "log/slog" + "slices" "strings" "time" @@ -112,15 +113,42 @@ func (c *Collector) Close() error { func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error { c.logger = logger.With(slog.String("collector", Name)) - var errs []error + names := make([]string, 0, len(c.config.Objects)) + errs := make([]error, 0, len(c.config.Objects)) for i, object := range c.config.Objects { if object.Name == "" { return fmt.Errorf("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 { + if counter.Name == "" { + errs = append(errs, errors.New("counter name is required")) + + 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) if counter.Metric == "" { diff --git a/internal/collector/performancecounter/performancecounter_test_test.go b/internal/collector/performancecounter/performancecounter_test_test.go index 6424156d3..c63371c07 100644 --- a/internal/collector/performancecounter/performancecounter_test_test.go +++ b/internal/collector/performancecounter/performancecounter_test_test.go @@ -49,31 +49,48 @@ func TestCollector(t *testing.T) { t.Parallel() for _, tc := range []struct { + name string object string instances []string instanceLabel string + buildErr error counters []performancecounter.Counter expectedMetrics *regexp.Regexp }{ { + name: "memory", object: "Memory", instances: nil, + buildErr: 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: "process", object: "Process", instances: []string{"*"}, + buildErr: nil, 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: nil, 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+]+`), }, + { + name: "", + object: "Processor Information", + instances: nil, + instanceLabel: "", + buildErr: fmt.Errorf("object name is empty"), + counters: nil, + expectedMetrics: nil, + }, } { t.Run(tc.object, func(t *testing.T) { t.Parallel() @@ -91,6 +108,12 @@ func TestCollector(t *testing.T) { logger := slog.New(slog.NewTextHandler(io.Discard, nil)) err := perfDataCollector.Build(logger, nil) + if tc.buildErr != nil { + require.ErrorIs(t, err, tc.buildErr) + + return + } + require.NoError(t, err) registry := prometheus.NewRegistry()