mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-10-19 19:19:04 +02:00
collector: publish base units and _total counter names (#2743)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
@@ -25,7 +25,6 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/prometheus-community/windows_exporter/internal/labelcache"
|
||||
@@ -34,7 +33,6 @@ import (
|
||||
"github.com/prometheus-community/windows_exporter/internal/scrapecache"
|
||||
"github.com/prometheus-community/windows_exporter/internal/utils/recovery"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type perfCollector[T any] interface {
|
||||
@@ -78,11 +76,10 @@ type Collector struct {
|
||||
// labels keeps the label pairs of the series of the last scrape. The
|
||||
// counters change on every scrape, but their process labels stay the
|
||||
// same while the process runs.
|
||||
labels labelcache.Cache
|
||||
config Config
|
||||
logger *slog.Logger
|
||||
perfFrequency float64
|
||||
closeFns []func()
|
||||
labels labelcache.Cache
|
||||
config Config
|
||||
logger *slog.Logger
|
||||
closeFns []func()
|
||||
// perfQuery samples the counters of all enabled sub-collectors at once.
|
||||
// The .NET CLR counter provider serves one query at a time and walks
|
||||
// every .NET process for each, so separate queries wait for each other.
|
||||
@@ -231,16 +228,6 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
|
||||
}
|
||||
|
||||
c.logger = logger
|
||||
if slices.Contains(c.config.CollectorsEnabled, collectorClrJIT) || slices.Contains(c.config.CollectorsEnabled, collectorClrSecurity) {
|
||||
var frequency int64
|
||||
|
||||
ret, _, err := windows.NewLazySystemDLL("kernel32.dll").NewProc("QueryPerformanceFrequency").Call(uintptr(unsafe.Pointer(&frequency)))
|
||||
if ret == 0 || frequency <= 0 {
|
||||
return fmt.Errorf("QueryPerformanceFrequency: %w", err)
|
||||
}
|
||||
|
||||
c.perfFrequency = float64(frequency)
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ func (c *Collector) describeClrJIT() {
|
||||
nil,
|
||||
)
|
||||
c.timeInJit = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrJIT+"_jit_time_percent"),
|
||||
"Displays the percentage of time spent in JIT compilation. This counter is updated at the end of every JIT compilation phase. A JIT compilation phase occurs when a method and its dependencies are compiled.",
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrJIT+"_jit_time_ratio"),
|
||||
"Displays the ratio (0-1) of elapsed time spent in JIT compilation since the last JIT compilation phase. This counter is updated at the end of every JIT compilation phase. A JIT compilation phase occurs when a method and its dependencies are compiled.",
|
||||
[]string{"process"},
|
||||
nil,
|
||||
)
|
||||
@@ -68,6 +68,7 @@ type perfDataClrJIT struct {
|
||||
Occurrence int
|
||||
NumberofMethodsJitted float64 `perfdata:"# of Methods JITted"`
|
||||
PercentTimeinJit float64 `perfdata:"% Time in Jit"`
|
||||
PercentTimeinJitBase float64 `perfdata:"% Time in Jit,secondvalue"`
|
||||
StandardJitFailures float64 `perfdata:"Standard Jit Failures"`
|
||||
TotalNumberofILBytesJitted float64 `perfdata:"Total # of IL Bytes Jitted"`
|
||||
}
|
||||
@@ -94,16 +95,18 @@ func (c *Collector) collectClrJIT(ch chan<- prometheus.Metric, scrape *labelcach
|
||||
process.Name,
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
c.timeInJit,
|
||||
prometheus.GaugeValue,
|
||||
float64(process.PercentTimeinJit)/c.perfFrequency,
|
||||
process.Name,
|
||||
)
|
||||
if process.PercentTimeinJitBase > 0 {
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
c.timeInJit,
|
||||
prometheus.GaugeValue,
|
||||
process.PercentTimeinJit/process.PercentTimeinJitBase,
|
||||
process.Name,
|
||||
)
|
||||
}
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
c.standardJitFailures,
|
||||
prometheus.GaugeValue,
|
||||
prometheus.CounterValue,
|
||||
float64(process.StandardJitFailures),
|
||||
process.Name,
|
||||
)
|
||||
|
||||
@@ -95,8 +95,8 @@ func (c *Collector) describeClrMemory() {
|
||||
nil,
|
||||
)
|
||||
c.timeInGC = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrMemory+"_gc_time_percent"),
|
||||
"Displays the percentage of time that was spent performing a garbage collection in the last sample.",
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrMemory+"_gc_time_ratio"),
|
||||
"Displays the ratio (0-1) of elapsed time spent performing a garbage collection since the end of the last garbage collection cycle.",
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
@@ -304,7 +304,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
c.timeInGC,
|
||||
prometheus.GaugeValue,
|
||||
100*process.PercentTimeinGC/process.PercentTimeinGC_base,
|
||||
process.PercentTimeinGC/process.PercentTimeinGC_base,
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
)
|
||||
|
||||
@@ -34,8 +34,8 @@ func (c *Collector) describeClrSecurity() {
|
||||
nil,
|
||||
)
|
||||
c.timeInRTChecks = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrSecurity+"_rt_checks_time_percent"),
|
||||
"Displays the percentage of time spent performing runtime code access security checks in the last sample.",
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrSecurity+"_rt_checks_time_ratio"),
|
||||
"Displays the ratio (0-1) of time spent performing runtime code access security checks in the last sample.",
|
||||
[]string{"process"},
|
||||
nil,
|
||||
)
|
||||
@@ -64,12 +64,13 @@ func (c *Collector) buildClrSecurity() error {
|
||||
}
|
||||
|
||||
type perfDataClrSecurity struct {
|
||||
Name string
|
||||
Occurrence int
|
||||
NumberLinkTimeChecks float64 `perfdata:"# Link Time Checks"`
|
||||
PercentTimeinRTchecks float64 `perfdata:"% Time in RT checks"`
|
||||
StackWalkDepth float64 `perfdata:"Stack Walk Depth"`
|
||||
TotalRuntimeChecks float64 `perfdata:"Total Runtime Checks"`
|
||||
Name string
|
||||
Occurrence int
|
||||
NumberLinkTimeChecks float64 `perfdata:"# Link Time Checks"`
|
||||
PercentTimeinRTchecks float64 `perfdata:"% Time in RT checks"`
|
||||
PercentTimeinRTchecksBase float64 `perfdata:"% Time in RT checks,secondvalue"`
|
||||
StackWalkDepth float64 `perfdata:"Stack Walk Depth"`
|
||||
TotalRuntimeChecks float64 `perfdata:"Total Runtime Checks"`
|
||||
}
|
||||
|
||||
func (p *perfDataClrSecurity) instance() (*string, int) { return &p.Name, p.Occurrence }
|
||||
@@ -94,12 +95,14 @@ func (c *Collector) collectClrSecurity(ch chan<- prometheus.Metric, scrape *labe
|
||||
process.Name,
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
c.timeInRTChecks,
|
||||
prometheus.GaugeValue,
|
||||
float64(process.PercentTimeinRTchecks)/c.perfFrequency,
|
||||
process.Name,
|
||||
)
|
||||
if process.PercentTimeinRTchecksBase > 0 {
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
c.timeInRTChecks,
|
||||
prometheus.GaugeValue,
|
||||
process.PercentTimeinRTchecks/process.PercentTimeinRTchecksBase,
|
||||
process.Name,
|
||||
)
|
||||
}
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
c.stackWalkDepth,
|
||||
|
||||
@@ -60,12 +60,12 @@ func TestClrCounterPaths(t *testing.T) {
|
||||
}{
|
||||
{reflect.TypeFor[perfDataClrExceptions](), []string{"# of Exceps Thrown", "# of Filters / sec", "# of Finallys / sec", "Throw To Catch Depth / sec"}},
|
||||
{reflect.TypeFor[perfDataClrInterop](), []string{"# of CCWs", "# of Stubs", "# of marshalling"}},
|
||||
{reflect.TypeFor[perfDataClrJIT](), []string{"# of Methods Jitted", "% Time in Jit", "Standard Jit Failures", "Total # of IL Bytes Jitted"}},
|
||||
{reflect.TypeFor[perfDataClrJIT](), []string{"# of Methods Jitted", "% Time in Jit", "% Time in Jit,secondvalue", "Standard Jit Failures", "Total # of IL Bytes Jitted"}},
|
||||
{reflect.TypeFor[perfDataClrLoading](), []string{"Bytes in Loader Heap", "Current appdomains", "Current Assemblies", "Current Classes Loaded", "Total Appdomains", "Total appdomains unloaded", "Total Assemblies", "Total Classes Loaded", "Total # of Load Failures"}},
|
||||
{reflect.TypeFor[perfDataClrLocksAndThreads](), []string{"Current Queue Length", "# of current logical Threads", "# of current physical Threads", "# of current recognized threads", "# of total recognized threads", "Queue Length Peak", "Total # of Contentions"}},
|
||||
{reflect.TypeFor[perfDataClrMemory](), []string{"Allocated Bytes/sec", "Finalization Survivors", "Gen 0 heap size", "Gen 0 Promoted Bytes/Sec", "Gen 1 heap size", "Gen 1 Promoted Bytes/Sec", "Gen 2 heap size", "Large Object Heap size", "# GC Handles", "# Gen 0 Collections", "# Gen 1 Collections", "# Gen 2 Collections", "# Induced GC", "# of Pinned Objects", "# of Sink Blocks in use", "# Total committed Bytes", "# Total reserved Bytes", "% Time in GC", "% Time in GC,secondvalue", "Process ID"}},
|
||||
{reflect.TypeFor[perfDataClrRemoting](), []string{"Channels", "Context-Bound Classes Loaded", "Context-Bound Objects Alloc / sec", "Context Proxies", "Contexts", "Total Remote Calls"}},
|
||||
{reflect.TypeFor[perfDataClrSecurity](), []string{"# Link Time Checks", "% Time in RT checks", "Stack Walk Depth", "Total Runtime Checks"}},
|
||||
{reflect.TypeFor[perfDataClrSecurity](), []string{"# Link Time Checks", "% Time in RT checks", "% Time in RT checks,secondvalue", "Stack Walk Depth", "Total Runtime Checks"}},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.rowType.Name(), func(t *testing.T) {
|
||||
|
||||
@@ -136,7 +136,7 @@ func assertFixtureMetrics(t *testing.T, collect func(chan<- prometheus.Metric, *
|
||||
}
|
||||
|
||||
func TestClrExceptionsMetricCompatibility(t *testing.T) {
|
||||
c := &Collector{perfFrequency: 1000}
|
||||
c := &Collector{}
|
||||
c.describeClrExceptions()
|
||||
|
||||
row := perfDataClrExceptions{
|
||||
@@ -162,7 +162,7 @@ func TestClrExceptionsMetricCompatibility(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClrInteropMetricCompatibility(t *testing.T) {
|
||||
c := &Collector{perfFrequency: 1000}
|
||||
c := &Collector{}
|
||||
c.describeClrInterop()
|
||||
|
||||
row := perfDataClrInterop{
|
||||
@@ -186,12 +186,13 @@ func TestClrInteropMetricCompatibility(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClrJITMetricCompatibility(t *testing.T) {
|
||||
c := &Collector{perfFrequency: 1000}
|
||||
c := &Collector{}
|
||||
c.describeClrJIT()
|
||||
|
||||
row := perfDataClrJIT{
|
||||
NumberofMethodsJitted: 11,
|
||||
PercentTimeinJit: 50,
|
||||
PercentTimeinJitBase: 200,
|
||||
StandardJitFailures: 13,
|
||||
TotalNumberofILBytesJitted: 14,
|
||||
}
|
||||
@@ -205,14 +206,14 @@ func TestClrJITMetricCompatibility(t *testing.T) {
|
||||
c.perfClrJIT = &fixturePerfCollector[perfDataClrJIT]{rows: rows}
|
||||
assertFixtureMetrics(t, c.collectClrJIT, []expectedMetric{
|
||||
{"windows_netframework_clrjit_jit_methods_total", dto.MetricType_COUNTER, 11, ""},
|
||||
{"windows_netframework_clrjit_jit_time_percent", dto.MetricType_GAUGE, 0.05, ""},
|
||||
{"windows_netframework_clrjit_jit_standard_failures_total", dto.MetricType_GAUGE, 13, ""},
|
||||
{"windows_netframework_clrjit_jit_time_ratio", dto.MetricType_GAUGE, 0.25, ""},
|
||||
{"windows_netframework_clrjit_jit_standard_failures_total", dto.MetricType_COUNTER, 13, ""},
|
||||
{"windows_netframework_clrjit_jit_il_bytes_total", dto.MetricType_COUNTER, 14, ""},
|
||||
}, false)
|
||||
}
|
||||
|
||||
func TestClrLoadingMetricCompatibility(t *testing.T) {
|
||||
c := &Collector{perfFrequency: 1000}
|
||||
c := &Collector{}
|
||||
c.describeClrLoading()
|
||||
|
||||
row := perfDataClrLoading{
|
||||
@@ -248,7 +249,7 @@ func TestClrLoadingMetricCompatibility(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClrLocksAndThreadsMetricCompatibility(t *testing.T) {
|
||||
c := &Collector{perfFrequency: 1000}
|
||||
c := &Collector{}
|
||||
c.describeClrLocksAndThreads()
|
||||
|
||||
row := perfDataClrLocksAndThreads{
|
||||
@@ -280,7 +281,7 @@ func TestClrLocksAndThreadsMetricCompatibility(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClrMemoryMetricCompatibility(t *testing.T) {
|
||||
c := &Collector{perfFrequency: 1000}
|
||||
c := &Collector{}
|
||||
c.describeClrMemory()
|
||||
|
||||
row := perfDataClrMemory{
|
||||
@@ -331,12 +332,12 @@ func TestClrMemoryMetricCompatibility(t *testing.T) {
|
||||
{"windows_netframework_clrmemory_number_sink_blocksinuse", dto.MetricType_GAUGE, 27, ""},
|
||||
{"windows_netframework_clrmemory_committed_bytes", dto.MetricType_GAUGE, 24, ""},
|
||||
{"windows_netframework_clrmemory_reserved_bytes", dto.MetricType_GAUGE, 25, ""},
|
||||
{"windows_netframework_clrmemory_gc_time_percent", dto.MetricType_GAUGE, 25, ""},
|
||||
{"windows_netframework_clrmemory_gc_time_ratio", dto.MetricType_GAUGE, 0.25, ""},
|
||||
}, true)
|
||||
}
|
||||
|
||||
func TestClrRemotingMetricCompatibility(t *testing.T) {
|
||||
c := &Collector{perfFrequency: 1000}
|
||||
c := &Collector{}
|
||||
c.describeClrRemoting()
|
||||
|
||||
row := perfDataClrRemoting{
|
||||
@@ -366,14 +367,15 @@ func TestClrRemotingMetricCompatibility(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClrSecurityMetricCompatibility(t *testing.T) {
|
||||
c := &Collector{perfFrequency: 1000}
|
||||
c := &Collector{}
|
||||
c.describeClrSecurity()
|
||||
|
||||
row := perfDataClrSecurity{
|
||||
NumberLinkTimeChecks: 11,
|
||||
PercentTimeinRTchecks: 50,
|
||||
StackWalkDepth: 13,
|
||||
TotalRuntimeChecks: 14,
|
||||
NumberLinkTimeChecks: 11,
|
||||
PercentTimeinRTchecks: 50,
|
||||
PercentTimeinRTchecksBase: 200,
|
||||
StackWalkDepth: 13,
|
||||
TotalRuntimeChecks: 14,
|
||||
}
|
||||
rows := make([]perfDataClrSecurity, 0, 4)
|
||||
|
||||
@@ -385,7 +387,7 @@ func TestClrSecurityMetricCompatibility(t *testing.T) {
|
||||
c.perfClrSecurity = &fixturePerfCollector[perfDataClrSecurity]{rows: rows}
|
||||
assertFixtureMetrics(t, c.collectClrSecurity, []expectedMetric{
|
||||
{"windows_netframework_clrsecurity_link_time_checks_total", dto.MetricType_COUNTER, 11, ""},
|
||||
{"windows_netframework_clrsecurity_rt_checks_time_percent", dto.MetricType_GAUGE, 0.05, ""},
|
||||
{"windows_netframework_clrsecurity_rt_checks_time_ratio", dto.MetricType_GAUGE, 0.25, ""},
|
||||
{"windows_netframework_clrsecurity_stack_walk_depth", dto.MetricType_GAUGE, 13, ""},
|
||||
{"windows_netframework_clrsecurity_runtime_checks_total", dto.MetricType_COUNTER, 14, ""},
|
||||
}, false)
|
||||
@@ -403,7 +405,7 @@ func TestGCZeroBaseDoesNotPublishInvalidRatio(t *testing.T) {
|
||||
foundAllocated := false
|
||||
|
||||
for _, family := range families {
|
||||
require.NotEqual(t, "windows_netframework_clrmemory_gc_time_percent", family.GetName())
|
||||
require.NotEqual(t, "windows_netframework_clrmemory_gc_time_ratio", family.GetName())
|
||||
|
||||
if family.GetName() == "windows_netframework_clrmemory_allocated_bytes_total" {
|
||||
require.InDelta(t, 99.0, family.GetMetric()[0].GetCounter().GetValue(), 1e-12)
|
||||
@@ -415,6 +417,31 @@ func TestGCZeroBaseDoesNotPublishInvalidRatio(t *testing.T) {
|
||||
require.True(t, foundAllocated)
|
||||
}
|
||||
|
||||
func TestJITAndSecurityZeroBaseDoNotPublishInvalidRatio(t *testing.T) {
|
||||
c := &Collector{}
|
||||
c.describeClrJIT()
|
||||
c.describeClrSecurity()
|
||||
c.perfClrJIT = &fixturePerfCollector[perfDataClrJIT]{rows: []perfDataClrJIT{{Name: "worker", PercentTimeinJit: 50, NumberofMethodsJitted: 7}}}
|
||||
c.perfClrSecurity = &fixturePerfCollector[perfDataClrSecurity]{rows: []perfDataClrSecurity{{Name: "worker", PercentTimeinRTchecks: 50, TotalRuntimeChecks: 7}}}
|
||||
registry := prometheus.NewRegistry()
|
||||
registry.MustRegister(fixturePromCollector{collect: func(ch chan<- prometheus.Metric) {
|
||||
require.NoError(t, c.collectClrJIT(ch, nil, time.Second))
|
||||
require.NoError(t, c.collectClrSecurity(ch, nil, time.Second))
|
||||
}})
|
||||
families, err := registry.Gather()
|
||||
require.NoError(t, err)
|
||||
|
||||
names := make([]string, 0, len(families))
|
||||
for _, family := range families {
|
||||
names = append(names, family.GetName())
|
||||
}
|
||||
|
||||
require.Contains(t, names, "windows_netframework_clrjit_jit_methods_total")
|
||||
require.Contains(t, names, "windows_netframework_clrsecurity_runtime_checks_total")
|
||||
require.NotContains(t, names, "windows_netframework_clrjit_jit_time_ratio")
|
||||
require.NotContains(t, names, "windows_netframework_clrsecurity_rt_checks_time_ratio")
|
||||
}
|
||||
|
||||
// TestDuplicateProcessNames checks that processes with the same name, which
|
||||
// the CLR objects report with the same instance name, are published apart.
|
||||
func TestDuplicateProcessNames(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user