From c6ad8f479283ff6df7f9595878245625adbeea8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sat, 10 Oct 2026 19:43:31 +0200 Subject: [PATCH] collector: publish documented counter types for udp and hyperv metrics (#2661) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Claude Fable 5 Signed-off-by: Jan-Otto Kröpke --- docs/collector.udp.md | 2 +- .../hyperv/hyperv_legacy_network_adapter.go | 2 +- internal/collector/hyperv/hyperv_test.go | 23 +++++++++++++++++++ internal/collector/udp/udp.go | 2 +- internal/collector/udp/udp_test.go | 17 +++++++++++++- pkg/collector/collect.go | 2 +- pkg/collector/handler.go | 2 +- tools/e2e-output.txt | 2 +- 8 files changed, 45 insertions(+), 7 deletions(-) diff --git a/docs/collector.udp.md b/docs/collector.udp.md index 5eedb010..3ad247fa 100644 --- a/docs/collector.udp.md +++ b/docs/collector.udp.md @@ -16,7 +16,7 @@ None | Name | Description | Type | Labels | |-----------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|---------|--------| -| `windows_udp_datagram_datagram_no_port_total` | Number of received UDP datagrams for which there was no application at the destination port | counter | af | +| `windows_udp_datagram_no_port_total` | Number of received UDP datagrams for which there was no application at the destination port | counter | af | | `windows_udp_datagram_received_errors_total` | Number of received UDP datagrams that could not be delivered for reasons other than the lack of an application at the destination port | counter | af | | `windows_udp_datagram_received_total` | Number of UDP datagrams segments received | counter | af | | `windows_udp_datagram_sent_total` | Number of UDP datagrams segments sent | counter | af | diff --git a/internal/collector/hyperv/hyperv_legacy_network_adapter.go b/internal/collector/hyperv/hyperv_legacy_network_adapter.go index 8f86c91a..f5860e12 100644 --- a/internal/collector/hyperv/hyperv_legacy_network_adapter.go +++ b/internal/collector/hyperv/hyperv_legacy_network_adapter.go @@ -106,7 +106,7 @@ func (c *Collector) collectLegacyNetworkAdapter(ch chan<- prometheus.Metric) err for _, data := range c.perfDataObjectLegacyNetworkAdapter { ch <- prometheus.MustNewConstMetric( c.legacyNetworkAdapterBytesDropped, - prometheus.GaugeValue, + prometheus.CounterValue, data.LegacyNetworkAdapterBytesDropped, data.Name, ) diff --git a/internal/collector/hyperv/hyperv_test.go b/internal/collector/hyperv/hyperv_test.go index 3faa71d4..a280a4ce 100644 --- a/internal/collector/hyperv/hyperv_test.go +++ b/internal/collector/hyperv/hyperv_test.go @@ -75,6 +75,29 @@ func TestCollectorVirtualStorageDeviceLatency(t *testing.T) { } } +// TestCollectorLegacyNetworkAdapter checks that the legacy network adapter +// metrics are published with the counter type documented in +// docs/collector.hyperv.md; bytes_dropped_total used to be published as a +// gauge. The CI fixture VM carries a legacy network adapter. +func TestCollectorLegacyNetworkAdapter(t *testing.T) { + metrics := testutils.TestCollector(t, hyperv.New, &hyperv.Config{CollectorsEnabled: []string{"legacy_network_adapter"}}) + + for _, name := range []string{ + "windows_hyperv_legacy_network_adapter_bytes_dropped_total", + "windows_hyperv_legacy_network_adapter_bytes_received_total", + "windows_hyperv_legacy_network_adapter_bytes_sent_total", + "windows_hyperv_legacy_network_adapter_frames_dropped_total", + "windows_hyperv_legacy_network_adapter_frames_received_total", + "windows_hyperv_legacy_network_adapter_frames_sent_total", + } { + testutils.RequireFixtureMetric(t, metrics, hyperv.Name, name, nil) + + if family, ok := metrics[name]; ok { + require.Equal(t, dto.MetricType_COUNTER, family.GetType(), name) + } + } +} + // TestCloseReleasesQuery ensures Close releases the PDH queries of the sub-collectors. func TestCloseReleasesQuery(t *testing.T) { t.Parallel() diff --git a/internal/collector/udp/udp.go b/internal/collector/udp/udp.go index d534b363..613b1f51 100644 --- a/internal/collector/udp/udp.go +++ b/internal/collector/udp/udp.go @@ -171,7 +171,7 @@ func (c *Collector) writeUDPCounters(ch chan<- prometheus.Metric, metrics []perf ch <- prometheus.MustNewConstMetric( c.datagramsReceivedTotal, - prometheus.GaugeValue, + prometheus.CounterValue, metrics[0].DatagramsReceivedPerSec, af, ) diff --git a/internal/collector/udp/udp_test.go b/internal/collector/udp/udp_test.go index 05c11b9b..71b1cc0c 100644 --- a/internal/collector/udp/udp_test.go +++ b/internal/collector/udp/udp_test.go @@ -22,6 +22,8 @@ import ( "github.com/prometheus-community/windows_exporter/internal/collector/udp" "github.com/prometheus-community/windows_exporter/internal/utils/testutils" + dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/require" ) func BenchmarkCollector(b *testing.B) { @@ -29,5 +31,18 @@ func BenchmarkCollector(b *testing.B) { } func TestCollector(t *testing.T) { - testutils.TestCollector(t, udp.New, nil) + metrics := testutils.TestCollector(t, udp.New, nil) + + // All four datagram totals are cumulative raw PDH values and documented as + // counters in docs/collector.udp.md; datagram_received_total used to be + // published as a gauge. + for _, name := range []string{ + "windows_udp_datagram_no_port_total", + "windows_udp_datagram_received_errors_total", + "windows_udp_datagram_received_total", + "windows_udp_datagram_sent_total", + } { + require.Contains(t, metrics, name) + require.Equal(t, dto.MetricType_COUNTER, metrics[name].GetType(), name) + } } diff --git a/pkg/collector/collect.go b/pkg/collector/collect.go index 39a00481..67d0a280 100644 --- a/pkg/collector/collect.go +++ b/pkg/collector/collect.go @@ -260,7 +260,7 @@ func logCollectorResult(ctx context.Context, logger *slog.Logger, name string, e if err != nil { if !expectedCollectionError(err) { if errors.Is(err, pdh.ErrPerformanceCounterNotInitialized) { - err = fmt.Errorf("%w. Check application logs from initialization pharse for more information", err) + err = fmt.Errorf("%w. Check application logs from initialization phase for more information", err) } logger.LogAttrs(ctx, slog.LevelWarn, diff --git a/pkg/collector/handler.go b/pkg/collector/handler.go index 4abcc37f..86ff7c13 100644 --- a/pkg/collector/handler.go +++ b/pkg/collector/handler.go @@ -30,7 +30,7 @@ import ( var _ prometheus.Collector = (*Handler)(nil) // scrapeSlot admits one scrape at a time, across all collections. -// We are expose metrics directly from the memory region of the Win32 API. +// We expose metrics directly from the memory region of the Win32 API. // We should not allow more than one request at a time. // //nolint:gochecknoglobals diff --git a/tools/e2e-output.txt b/tools/e2e-output.txt index 5a14713d..dea124ab 100644 --- a/tools/e2e-output.txt +++ b/tools/e2e-output.txt @@ -436,6 +436,6 @@ windows_service_state{name="Themes",state="stopped"} 0 # HELP windows_udp_datagram_received_errors_total Number of received UDP datagrams that could not be delivered for reasons other than the lack of an application at the destination port # TYPE windows_udp_datagram_received_errors_total counter # HELP windows_udp_datagram_received_total UDP datagrams are delivered to UDP users -# TYPE windows_udp_datagram_received_total gauge +# TYPE windows_udp_datagram_received_total counter # HELP windows_udp_datagram_sent_total UDP datagrams are sent from the entity # TYPE windows_udp_datagram_sent_total counter