Skip to content

Commit

Permalink
performancecounter: fix panic with counter names having brackets (#1822)
Browse files Browse the repository at this point in the history
Signed-off-by: Jan-Otto Kröpke <[email protected]>
  • Loading branch information
jkroepke authored Dec 27, 2024
1 parent 7838655 commit 81ea4c6
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 28 deletions.
48 changes: 28 additions & 20 deletions internal/collector/performancecounter/performancecounter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"log/slog"
"reflect"
"regexp"
"slices"
"strings"
"time"
Expand All @@ -34,6 +35,8 @@ import (

const Name = "performancecounter"

var reNonAlphaNum = regexp.MustCompile(`[^a-zA-Z0-9]`)

type Config struct {
Objects []Object `yaml:"objects"`
}
Expand All @@ -51,8 +54,6 @@ type Collector struct {

objects []Object

metricNameReplacer *strings.Replacer

// meta
subCollectorScrapeDurationDesc *prometheus.Desc
subCollectorScrapeSuccessDesc *prometheus.Desc
Expand Down Expand Up @@ -115,15 +116,6 @@ 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))

Expand Down Expand Up @@ -152,7 +144,7 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {

for j, counter := range object.Counters {
if counter.Metric == "" {
c.config.Objects[i].Counters[j].Metric = c.sanitizeMetricName(
c.config.Objects[i].Counters[j].Metric = sanitizeMetricName(
fmt.Sprintf("%s_%s_%s_%s", types.Namespace, Name, object.Object, counter.Name),
)
}
Expand All @@ -171,11 +163,27 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
}

counters = append(counters, counter.Name)
fields = append(fields, reflect.StructField{
Name: strings.ToUpper(c.sanitizeMetricName(counter.Name)),
Type: reflect.TypeOf(float64(0)),
Tag: reflect.StructTag(fmt.Sprintf(`perfdata:"%s"`, counter.Name)),
})

field, err := func(name string) (_ reflect.StructField, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("failed to create field for %s: %v", name, r)
}
}()

return reflect.StructField{
Name: strings.ToUpper(sanitizeMetricName(name)),
Type: reflect.TypeOf(float64(0)),
Tag: reflect.StructTag(fmt.Sprintf(`perfdata:"%s"`, name)),
}, nil
}(counter.Name)
if err != nil {
errs = append(errs, err)

continue
}

fields = append(fields, field)
}

if object.Instances != nil {
Expand Down Expand Up @@ -276,7 +284,7 @@ func (c *Collector) collectObject(ch chan<- prometheus.Metric, perfDataObject Ob
for _, counter := range perfDataObject.Counters {
val := reflect.ValueOf(sliceValue).Index(i)

field := val.FieldByName(strings.ToUpper(c.sanitizeMetricName(counter.Name)))
field := val.FieldByName(strings.ToUpper(sanitizeMetricName(counter.Name)))
if !field.IsValid() {
errs = append(errs, fmt.Errorf("%s not found in collected data", counter.Name))

Expand Down Expand Up @@ -355,6 +363,6 @@ func (c *Collector) collectObject(ch chan<- prometheus.Metric, perfDataObject Ob
return errors.Join(errs...)
}

func (c *Collector) sanitizeMetricName(name string) string {
return strings.Trim(c.metricNameReplacer.Replace(strings.ToLower(name)), "_")
func sanitizeMetricName(name string) string {
return strings.Trim(reNonAlphaNum.ReplaceAllString(strings.ToLower(name), "_"), "_")
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"testing"

"github.com/prometheus-community/windows_exporter/internal/collector/performancecounter"
"github.com/prometheus-community/windows_exporter/internal/pdh"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -126,6 +127,14 @@ windows_performancecounter_processor_information_processor_time\{core="0,0",stat
counters: []performancecounter.Counter{{Name: "Available Bytes", Type: "gauge"}, {Name: "Available Bytes", Type: "gauge"}},
expectedMetrics: nil,
},
{
name: "counter with spaces and brackets",
object: "invalid",
instances: nil,
buildErr: pdh.NewPdhError(pdh.CstatusNoObject).Error(),
counters: []performancecounter.Counter{{Name: "Total Memory Usage --- Non-Paged Pool", Type: "counter"}, {Name: "Max Session Input Delay (ms)", Type: "counter"}},
expectedMetrics: nil,
},
} {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
Expand Down
19 changes: 11 additions & 8 deletions internal/collector/vmware/vmware.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,19 @@ func (c *Collector) Close() error {
}

func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
var err error
var (
err error
errs []error
)

c.perfDataCollectorCPU, err = pdh.NewCollector[perfDataCounterValuesCPU]("VM Processor", pdh.InstancesTotal)
if err != nil {
return fmt.Errorf("failed to create VM Processor collector: %w", err)
errs = append(errs, fmt.Errorf("failed to create VM Processor collector: %w", err))
}

c.perfDataCollectorMemory, err = pdh.NewCollector[perfDataCounterValuesMemory]("VM Memory", nil)
if err != nil {
errs = append(errs, fmt.Errorf("failed to create VM Memory collector: %w", err))
}

c.cpuLimitMHz = prometheus.NewDesc(
Expand Down Expand Up @@ -143,11 +151,6 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
nil,
)

c.perfDataCollectorMemory, err = pdh.NewCollector[perfDataCounterValuesMemory]("VM Memory", nil)
if err != nil {
return fmt.Errorf("failed to create VM Memory collector: %w", err)
}

c.memActive = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_active_bytes"),
"The estimated amount of memory the virtual machine is actively using.",
Expand Down Expand Up @@ -221,7 +224,7 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
nil,
)

return nil
return errors.Join(errs...)
}

// Collect sends the metric values for each metric
Expand Down

0 comments on commit 81ea4c6

Please sign in to comment.