From c7fa244d0e207c4a233f8b91b0c951b2d982a52f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Fri, 9 Oct 2026 08:56:47 +0200 Subject: [PATCH] logical_disk: deprecate cumulative avg_*_requests_queued gauges (#2563) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Claude Opus 5.5 Signed-off-by: Jan-Otto Kröpke --- docs/collector.logical_disk.md | 50 ++++++++++++------- .../collector/logical_disk/logical_disk.go | 10 +++- .../logical_disk/logical_disk_test.go | 25 ++++++++++ internal/utils/testutils/testutils.go | 22 ++++++++ tools/e2e-output.txt | 4 +- 5 files changed, 89 insertions(+), 22 deletions(-) diff --git a/docs/collector.logical_disk.md b/docs/collector.logical_disk.md index 462583af..10b40b64 100644 --- a/docs/collector.logical_disk.md +++ b/docs/collector.logical_disk.md @@ -25,24 +25,38 @@ Comma-separated list of collectors to use. Available collectors: metrics, bitloc ## Metrics -| Name | Description | Type | Labels | -|--------------------------------------------------|----------------------------------------------------------------------------------------------------|---------|-------------------------------------------------------------------| -| `windows_logical_disk_info` | A metric with a constant '1' value labeled with logical disk information | gauge | `disk`,`filesystem`,`serial_number`,`volume`,`volume_name`,`type` | -| `windows_logical_disk_requests_queued` | Number of requests outstanding on the disk at the time the performance data is collected | gauge | `volume` | -| `windows_logical_disk_avg_read_requests_queued` | Average number of read requests that were queued for the selected disk during the sample interval | gauge | `volume` | -| `windows_logical_disk_avg_write_requests_queued` | Average number of write requests that were queued for the selected disk during the sample interval | gauge | `volume` | -| `windows_logical_disk_read_bytes_total` | Rate at which bytes are transferred from the disk during read operations | counter | `volume` | -| `windows_logical_disk_reads_total` | Rate of read operations on the disk | counter | `volume` | -| `windows_logical_disk_write_bytes_total` | Rate at which bytes are transferred to the disk during write operations | counter | `volume` | -| `windows_logical_disk_writes_total` | Rate of write operations on the disk | counter | `volume` | -| `windows_logical_disk_read_seconds_total` | Seconds the disk was busy servicing read requests | counter | `volume` | -| `windows_logical_disk_write_seconds_total` | Seconds the disk was busy servicing write requests | counter | `volume` | -| `windows_logical_disk_free_bytes` | Unused space of the disk in bytes (not real time, updates every 10-15 min) | gauge | `volume` | -| `windows_logical_disk_size_bytes` | Total size of the disk in bytes (not real time, updates every 10-15 min) | gauge | `volume` | -| `windows_logical_disk_idle_seconds_total` | Seconds the disk was idle (not servicing read/write requests) | counter | `volume` | -| `windows_logical_disk_split_ios_total` | Number of I/Os to the disk split into multiple I/Os | counter | `volume` | -| `windows_logical_disk_readonly` | Whether the logical disk is read-only | gauge | `volume` | -| `windows_logical_disk_bitlocker_status` | BitLocker status for the logical disk | gauge | `volume`,`status` | +| Name | Description | Type | Labels | +|--------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------|---------|-------------------------------------------------------------------| +| `windows_logical_disk_info` | A metric with a constant '1' value labeled with logical disk information | gauge | `disk`,`filesystem`,`serial_number`,`volume`,`volume_name`,`type` | +| `windows_logical_disk_requests_queued` | Number of requests outstanding on the disk at the time the performance data is collected | gauge | `volume` | +| `windows_logical_disk_avg_read_requests_queued` | **Deprecated:** use `rate(windows_logical_disk_read_seconds_total)`. Cumulative read queue length in seconds, see below | gauge | `volume` | +| `windows_logical_disk_avg_write_requests_queued` | **Deprecated:** use `rate(windows_logical_disk_write_seconds_total)`. Cumulative write queue length in seconds, see below | gauge | `volume` | +| `windows_logical_disk_read_bytes_total` | Rate at which bytes are transferred from the disk during read operations | counter | `volume` | +| `windows_logical_disk_reads_total` | Rate of read operations on the disk | counter | `volume` | +| `windows_logical_disk_write_bytes_total` | Rate at which bytes are transferred to the disk during write operations | counter | `volume` | +| `windows_logical_disk_writes_total` | Rate of write operations on the disk | counter | `volume` | +| `windows_logical_disk_read_seconds_total` | Seconds the disk was busy servicing read requests | counter | `volume` | +| `windows_logical_disk_write_seconds_total` | Seconds the disk was busy servicing write requests | counter | `volume` | +| `windows_logical_disk_free_bytes` | Unused space of the disk in bytes (not real time, updates every 10-15 min) | gauge | `volume` | +| `windows_logical_disk_size_bytes` | Total size of the disk in bytes (not real time, updates every 10-15 min) | gauge | `volume` | +| `windows_logical_disk_idle_seconds_total` | Seconds the disk was idle (not servicing read/write requests) | counter | `volume` | +| `windows_logical_disk_split_ios_total` | Number of I/Os to the disk split into multiple I/Os | counter | `volume` | +| `windows_logical_disk_readonly` | Whether the logical disk is read-only | gauge | `volume` | +| `windows_logical_disk_bitlocker_status` | BitLocker status for the logical disk | gauge | `volume`,`status` | + +### Deprecated average queue length metrics +`Avg. Disk Read Queue Length` and `Avg. Disk Write Queue Length` are averages that Windows computes from two samples. +windows_exporter reads their raw values, which only grow: the queue length summed over time, in seconds. +These raw values are the same as the ones of `% Disk Read Time` and `% Disk Write Time`, +which are exposed as `windows_logical_disk_read_seconds_total` and `windows_logical_disk_write_seconds_total`. + +`windows_logical_disk_avg_read_requests_queued` and `windows_logical_disk_avg_write_requests_queued` keep exposing +the raw values as gauges for compatibility. They are deprecated and will be removed in a future release. +Use the rate of the `*_seconds_total` counters for the average queue length instead: +``` +rate(windows_logical_disk_read_seconds_total[5m]) +rate(windows_logical_disk_write_seconds_total[5m]) +``` ### Warning about size metrics The `free_bytes` and `size_bytes` metrics are not updated in real time and might have a delay of 10-15min. diff --git a/internal/collector/logical_disk/logical_disk.go b/internal/collector/logical_disk/logical_disk.go index 9599791a..37e95e35 100644 --- a/internal/collector/logical_disk/logical_disk.go +++ b/internal/collector/logical_disk/logical_disk.go @@ -232,14 +232,16 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error { c.avgReadQueue = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "avg_read_requests_queued"), - "Average number of read requests that were queued for the selected disk during the sample interval (LogicalDisk.AvgDiskReadQueueLength)", + "DEPRECATED: use rate(windows_logical_disk_read_seconds_total). "+ + "Cumulative read queue length in seconds, identical to windows_logical_disk_read_seconds_total (LogicalDisk.AvgDiskReadQueueLength)", []string{"volume"}, nil, ) c.avgWriteQueue = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "avg_write_requests_queued"), - "Average number of write requests that were queued for the selected disk during the sample interval (LogicalDisk.AvgDiskWriteQueueLength)", + "DEPRECATED: use rate(windows_logical_disk_write_seconds_total). "+ + "Cumulative write queue length in seconds, identical to windows_logical_disk_write_seconds_total (LogicalDisk.AvgDiskWriteQueueLength)", []string{"volume"}, nil, ) @@ -399,6 +401,10 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric, _ time.Duration) error data.Name, ) + // The raw value of a PERF_COUNTER_100NS_QUEUELEN_TYPE counter is the queue length + // integrated over time in 100ns ticks, not an average. It shares its source with + // "% Disk Read/Write Time". Kept as gauge for compatibility: as a counter, + // OpenMetrics exposition would rename the series to *_total. ch <- prometheus.MustNewConstMetric( c.avgReadQueue, prometheus.GaugeValue, diff --git a/internal/collector/logical_disk/logical_disk_test.go b/internal/collector/logical_disk/logical_disk_test.go index 8a98138d..37b611b9 100644 --- a/internal/collector/logical_disk/logical_disk_test.go +++ b/internal/collector/logical_disk/logical_disk_test.go @@ -46,6 +46,31 @@ func TestCollector(t *testing.T) { }) } +// TestCollectorAvgQueueIsCumulative checks that the raw "Avg. Disk Read/Write Queue Length" +// values are not averages, but the same cumulative values as "% Disk Read/Write Time". +// This is why the avg_*_requests_queued metrics are kept only for compatibility. +func TestCollectorAvgQueueIsCumulative(t *testing.T) { + metrics := testutils.TestCollector(t, logical_disk.New, &logical_disk.Config{ + CollectorsEnabled: logical_disk.ConfigDefaults.CollectorsEnabled, + VolumeInclude: types.RegExpAny, + }) + + for deprecated, replacement := range map[string]string{ + "windows_logical_disk_avg_read_requests_queued": "windows_logical_disk_read_seconds_total", + "windows_logical_disk_avg_write_requests_queued": "windows_logical_disk_write_seconds_total", + } { + want := testutils.MetricValuesByLabel(metrics, replacement, "volume") + values := testutils.MetricValuesByLabel(metrics, deprecated, "volume") + + require.NotEmpty(t, values, "%s was not emitted", deprecated) + + for volume, got := range values { + require.Contains(t, want, volume) + require.InDelta(t, want[volume], got, 1e-6, "%s{volume=%q}", deprecated, volume) + } + } +} + func TestCollectorBitlocker(t *testing.T) { metrics := testutils.TestCollector(t, logical_disk.New, &logical_disk.Config{ CollectorsEnabled: []string{"metrics", "bitlocker_status"}, diff --git a/internal/utils/testutils/testutils.go b/internal/utils/testutils/testutils.go index 13d61902..171ef7db 100644 --- a/internal/utils/testutils/testutils.go +++ b/internal/utils/testutils/testutils.go @@ -256,6 +256,28 @@ func RequireFixtureMetric(t *testing.T, families map[string]*dto.MetricFamily, c return nil } +// MetricValuesByLabel returns the gauge or counter values of metricName, +// keyed by the value of the label labelName. +func MetricValuesByLabel(families map[string]*dto.MetricFamily, metricName, labelName string) map[string]float64 { + values := make(map[string]float64) + + for _, metric := range families[metricName].GetMetric() { + for _, label := range metric.GetLabel() { + if label.GetName() != labelName { + continue + } + + if metric.GetCounter() != nil { + values[label.GetValue()] = metric.GetCounter().GetValue() + } else { + values[label.GetValue()] = metric.GetGauge().GetValue() + } + } + } + + return values +} + func noDataOnly(err error) bool { if joined, ok := err.(interface{ Unwrap() []error }); ok { for _, cause := range joined.Unwrap() { diff --git a/tools/e2e-output.txt b/tools/e2e-output.txt index 162435c5..5a14713d 100644 --- a/tools/e2e-output.txt +++ b/tools/e2e-output.txt @@ -147,9 +147,9 @@ windows_exporter_collector_timeout{collector="time"} 0 windows_exporter_collector_timeout{collector="udp"} 0 # HELP windows_exporter_scrape_duration_seconds windows_exporter: Total scrape duration. # TYPE windows_exporter_scrape_duration_seconds gauge -# HELP windows_logical_disk_avg_read_requests_queued Average number of read requests that were queued for the selected disk during the sample interval (LogicalDisk.AvgDiskReadQueueLength) +# HELP windows_logical_disk_avg_read_requests_queued DEPRECATED: use rate(windows_logical_disk_read_seconds_total). Cumulative read queue length in seconds, identical to windows_logical_disk_read_seconds_total (LogicalDisk.AvgDiskReadQueueLength) # TYPE windows_logical_disk_avg_read_requests_queued gauge -# HELP windows_logical_disk_avg_write_requests_queued Average number of write requests that were queued for the selected disk during the sample interval (LogicalDisk.AvgDiskWriteQueueLength) +# HELP windows_logical_disk_avg_write_requests_queued DEPRECATED: use rate(windows_logical_disk_write_seconds_total). Cumulative write queue length in seconds, identical to windows_logical_disk_write_seconds_total (LogicalDisk.AvgDiskWriteQueueLength) # TYPE windows_logical_disk_avg_write_requests_queued gauge # HELP windows_logical_disk_free_bytes Free space in bytes, updates every 10-15 min (LogicalDisk.PercentFreeSpace) # TYPE windows_logical_disk_free_bytes gauge