mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-02-23 05:06:36 +00:00
performancecounter: fix panic with counter names having brackets (#1822)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
@@ -20,6 +20,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -34,6 +35,8 @@ import (
|
|||||||
|
|
||||||
const Name = "performancecounter"
|
const Name = "performancecounter"
|
||||||
|
|
||||||
|
var reNonAlphaNum = regexp.MustCompile(`[^a-zA-Z0-9]`)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Objects []Object `yaml:"objects"`
|
Objects []Object `yaml:"objects"`
|
||||||
}
|
}
|
||||||
@@ -51,8 +54,6 @@ type Collector struct {
|
|||||||
|
|
||||||
objects []Object
|
objects []Object
|
||||||
|
|
||||||
metricNameReplacer *strings.Replacer
|
|
||||||
|
|
||||||
// meta
|
// meta
|
||||||
subCollectorScrapeDurationDesc *prometheus.Desc
|
subCollectorScrapeDurationDesc *prometheus.Desc
|
||||||
subCollectorScrapeSuccessDesc *prometheus.Desc
|
subCollectorScrapeSuccessDesc *prometheus.Desc
|
||||||
@@ -115,15 +116,6 @@ func (c *Collector) Close() error {
|
|||||||
|
|
||||||
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
|
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
|
||||||
c.logger = logger.With(slog.String("collector", Name))
|
c.logger = logger.With(slog.String("collector", Name))
|
||||||
|
|
||||||
c.metricNameReplacer = strings.NewReplacer(
|
|
||||||
".", "",
|
|
||||||
"%", "",
|
|
||||||
"/", "_",
|
|
||||||
" ", "_",
|
|
||||||
"-", "_",
|
|
||||||
)
|
|
||||||
|
|
||||||
c.objects = make([]Object, 0, len(c.config.Objects))
|
c.objects = make([]Object, 0, len(c.config.Objects))
|
||||||
names := make([]string, 0, len(c.config.Objects))
|
names := make([]string, 0, len(c.config.Objects))
|
||||||
|
|
||||||
@@ -152,7 +144,7 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
|
|||||||
|
|
||||||
for j, counter := range object.Counters {
|
for j, counter := range object.Counters {
|
||||||
if counter.Metric == "" {
|
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),
|
fmt.Sprintf("%s_%s_%s_%s", types.Namespace, Name, object.Object, counter.Name),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -171,11 +163,27 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
counters = append(counters, counter.Name)
|
counters = append(counters, counter.Name)
|
||||||
fields = append(fields, reflect.StructField{
|
|
||||||
Name: strings.ToUpper(c.sanitizeMetricName(counter.Name)),
|
field, err := func(name string) (_ reflect.StructField, err error) {
|
||||||
Type: reflect.TypeOf(float64(0)),
|
defer func() {
|
||||||
Tag: reflect.StructTag(fmt.Sprintf(`perfdata:"%s"`, counter.Name)),
|
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 {
|
if object.Instances != nil {
|
||||||
@@ -276,7 +284,7 @@ func (c *Collector) collectObject(ch chan<- prometheus.Metric, perfDataObject Ob
|
|||||||
for _, counter := range perfDataObject.Counters {
|
for _, counter := range perfDataObject.Counters {
|
||||||
val := reflect.ValueOf(sliceValue).Index(i)
|
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() {
|
if !field.IsValid() {
|
||||||
errs = append(errs, fmt.Errorf("%s not found in collected data", counter.Name))
|
errs = append(errs, fmt.Errorf("%s not found in collected data", counter.Name))
|
||||||
|
|
||||||
@@ -355,6 +363,6 @@ func (c *Collector) collectObject(ch chan<- prometheus.Metric, perfDataObject Ob
|
|||||||
return errors.Join(errs...)
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Collector) sanitizeMetricName(name string) string {
|
func sanitizeMetricName(name string) string {
|
||||||
return strings.Trim(c.metricNameReplacer.Replace(strings.ToLower(name)), "_")
|
return strings.Trim(reNonAlphaNum.ReplaceAllString(strings.ToLower(name), "_"), "_")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/prometheus-community/windows_exporter/internal/collector/performancecounter"
|
"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"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -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"}},
|
counters: []performancecounter.Counter{{Name: "Available Bytes", Type: "gauge"}, {Name: "Available Bytes", Type: "gauge"}},
|
||||||
expectedMetrics: nil,
|
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.Run(tc.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|||||||
@@ -93,11 +93,19 @@ func (c *Collector) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) 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)
|
c.perfDataCollectorCPU, err = pdh.NewCollector[perfDataCounterValuesCPU]("VM Processor", pdh.InstancesTotal)
|
||||||
if err != nil {
|
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(
|
c.cpuLimitMHz = prometheus.NewDesc(
|
||||||
@@ -143,11 +151,6 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
|
|||||||
nil,
|
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(
|
c.memActive = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "mem_active_bytes"),
|
prometheus.BuildFQName(types.Namespace, Name, "mem_active_bytes"),
|
||||||
"The estimated amount of memory the virtual machine is actively using.",
|
"The estimated amount of memory the virtual machine is actively using.",
|
||||||
@@ -221,7 +224,7 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
|
|||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
return nil
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
|
|||||||
Reference in New Issue
Block a user