Skip to content

Commit

Permalink
add more stricter checks
Browse files Browse the repository at this point in the history
Signed-off-by: Jan-Otto Kröpke <[email protected]>
  • Loading branch information
jkroepke committed Dec 8, 2024
1 parent 93ccd23 commit d99d6b9
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 5 deletions.
47 changes: 43 additions & 4 deletions docs/collector.performancecounter.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -25,7 +44,8 @@ YAML:
<summary>Click to expand YAML schema</summary>
```yaml
- object: "Processor Information"
- name: cpu # free text name
object: "Processor Information" # Performance counter object name
instances: ["*"]
instance_label: "core"
counters:
Expand All @@ -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
Expand All @@ -51,6 +72,7 @@ YAML:
```json
[
{
"name": "cpu",
"object": "Processor Information",
"instances": [
"*"
Expand All @@ -74,6 +96,7 @@ YAML:
]
},
{
"name": "memory",
"object": "Memory",
"counters": [
{
Expand All @@ -86,6 +109,11 @@ YAML:
```
</details>

#### 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.
Expand Down Expand Up @@ -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
Expand Down
30 changes: 29 additions & 1 deletion internal/collector/performancecounter/performancecounter.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"errors"
"fmt"
"log/slog"
"slices"
"strings"
"time"

Expand Down Expand Up @@ -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")

Check failure on line 121 in internal/collector/performancecounter/performancecounter.go

View workflow job for this annotation

GitHub Actions / lint

fmt.Errorf can be replaced with errors.New (perfsprint)
}

if object.Object == "" {
errs = append(errs, fmt.Errorf("object %s: object is required", object.Name))

Check failure on line 125 in internal/collector/performancecounter/performancecounter.go

View workflow job for this annotation

GitHub Actions / lint

SA4010: this result of append is never used, except maybe in other appends (staticcheck)

continue
}

if slices.Contains(names, object.Name) {
errs = append(errs, fmt.Errorf("object %s: name is duplicated", object.Name))

Check failure on line 131 in internal/collector/performancecounter/performancecounter.go

View workflow job for this annotation

GitHub Actions / lint

SA4010: this result of append is never used, except maybe in other appends (staticcheck)

continue
}

names = append(names, object.Name)

counters := make([]string, 0, len(object.Counters))
for j, counter := range object.Counters {

Check failure on line 139 in internal/collector/performancecounter/performancecounter.go

View workflow job for this annotation

GitHub Actions / lint

ranges should only be cuddled with assignments used in the iteration (wsl)
if counter.Name == "" {
errs = append(errs, errors.New("counter name is required"))

Check failure on line 141 in internal/collector/performancecounter/performancecounter.go

View workflow job for this annotation

GitHub Actions / lint

SA4010: this result of append is never used, except maybe in other appends (staticcheck)

continue
}

if slices.Contains(counters, counter.Name) {
errs = append(errs, fmt.Errorf("counter name %s is duplicated", counter.Name))

Check failure on line 147 in internal/collector/performancecounter/performancecounter.go

View workflow job for this annotation

GitHub Actions / lint

SA4010: this result of append is never used, except maybe in other appends (staticcheck)

continue
}

counters = append(counters, counter.Name)

if counter.Metric == "" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"),

Check failure on line 90 in internal/collector/performancecounter/performancecounter_test_test.go

View workflow job for this annotation

GitHub Actions / lint

fmt.Errorf can be replaced with errors.New (perfsprint)
counters: nil,
expectedMetrics: nil,
},
} {
t.Run(tc.object, func(t *testing.T) {
t.Parallel()
Expand All @@ -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()
Expand Down

0 comments on commit d99d6b9

Please sign in to comment.