diff --git a/docs/collector.hyperv.md b/docs/collector.hyperv.md index 6335d811..6a7a34e4 100644 --- a/docs/collector.hyperv.md +++ b/docs/collector.hyperv.md @@ -86,17 +86,21 @@ Some metrics explained: https://learn.microsoft.com/en-us/archive/blogs/chrisavi | Name | Description | Type | Labels | |------------------------------------------------------------------------|-----------------------------------------------------------------------------------|---------|--------| -| `windows_hyperv_dynamic_memory_vm_added_bytes_total` | Represents the cumulative amount of memory added to the VM. | counter | `vm` | +| `windows_hyperv_dynamic_memory_vm_added_total` | Represents the cumulative amount of memory added to the VM. | counter | `vm` | | `windows_hyperv_dynamic_memory_vm_pressure_current_ratio` | Represents the current pressure in the VM. | gauge | `vm` | | `windows_hyperv_dynamic_memory_vm_guest_available_bytes` | Represents the current amount of available memory in the VM (reported by the VM). | gauge | `vm` | -| `windows_hyperv_dynamic_memory_vm_guest_visible_physical_memory_bytes` | Represents the amount of memory visible in the VM | gauge | `vm` | +| `windows_hyperv_dynamic_memory_vm_guest_visible_physical_memory_bytes` | Represents the amount of memory visible in the VM. | gauge | `vm` | | `windows_hyperv_dynamic_memory_vm_pressure_maximum_ratio` | Represents the maximum pressure band in the VM. | gauge | `vm` | | `windows_hyperv_dynamic_memory_vm_add_operations_total` | Represents the total number of add operations for the VM. | counter | `vm` | | `windows_hyperv_dynamic_memory_vm_remove_operations_total` | Represents the total number of remove operations for the VM. | counter | `vm` | | `windows_hyperv_dynamic_memory_vm_pressure_minimum_ratio` | Represents the minimum pressure band in the VM. | gauge | `vm` | -| `windows_hyperv_dynamic_memory_vm_physical` | Represents the current amount of memory in the VM. | gauge | `vm` | +| `windows_hyperv_dynamic_memory_vm_physical_bytes` | Represents the current amount of memory in the VM. | gauge | `vm` | | `windows_hyperv_dynamic_memory_vm_removed_bytes_total` | Represents the cumulative amount of memory removed from the VM. | counter | `vm` | +Windows reports no instance name for VMs managed by the Host Compute Service, such as the WSL 2 VM. +The first such VM has `vm="(unknown)"`, further ones `vm="(unknown)#1"`, `vm="(unknown)#2"` and so on. +The suffix only keeps the series unique and does not identify the VM across scrapes. + ### Hyper-V Host Metrics about the host's CPU allocation. Processor counts per virtual machine are diff --git a/internal/collector/hyperv/hyperv_dynamic_memory_vm.go b/internal/collector/hyperv/hyperv_dynamic_memory_vm.go index 344882a6..be793fa7 100644 --- a/internal/collector/hyperv/hyperv_dynamic_memory_vm.go +++ b/internal/collector/hyperv/hyperv_dynamic_memory_vm.go @@ -19,6 +19,7 @@ package hyperv import ( "fmt" + "strings" "github.com/prometheus-community/windows_exporter/internal/osversion" "github.com/prometheus-community/windows_exporter/internal/pdh" @@ -88,7 +89,7 @@ func (c *Collector) buildDynamicMemoryVM() error { ) c.vmMemoryGuestVisiblePhysicalMemory = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "dynamic_memory_vm_guest_visible_physical_memory_bytes"), - "Represents the amount of memory visible in the VM.'", + "Represents the amount of memory visible in the VM.", []string{"vm"}, nil, ) @@ -139,18 +140,20 @@ func (c *Collector) collectDynamicMemoryVM(ch chan<- prometheus.Metric) error { } for _, data := range c.perfDataObjectDynamicMemoryVM { + vmName := dynamicMemoryVMName(data.Name) + ch <- prometheus.MustNewConstMetric( c.vmMemoryAddedMemory, prometheus.CounterValue, utils.MBToBytes(data.VmMemoryAddedMemory), - data.Name, + vmName, ) ch <- prometheus.MustNewConstMetric( c.vmMemoryCurrentPressure, prometheus.GaugeValue, utils.PercentageToRatio(data.VmMemoryCurrentPressure), - data.Name, + vmName, ) if osversion.Build() >= osversion.LTSC2022 { @@ -158,7 +161,7 @@ func (c *Collector) collectDynamicMemoryVM(ch chan<- prometheus.Metric) error { c.vmMemoryGuestAvailableMemory, prometheus.GaugeValue, utils.MBToBytes(data.VmMemoryGuestAvailableMemory), - data.Name, + vmName, ) } @@ -166,51 +169,72 @@ func (c *Collector) collectDynamicMemoryVM(ch chan<- prometheus.Metric) error { c.vmMemoryGuestVisiblePhysicalMemory, prometheus.GaugeValue, utils.MBToBytes(data.VmMemoryGuestVisiblePhysicalMemory), - data.Name, + vmName, ) ch <- prometheus.MustNewConstMetric( c.vmMemoryMaximumPressure, prometheus.GaugeValue, utils.PercentageToRatio(data.VmMemoryMaximumPressure), - data.Name, + vmName, ) ch <- prometheus.MustNewConstMetric( c.vmMemoryMemoryAddOperations, prometheus.CounterValue, data.VmMemoryMemoryAddOperations, - data.Name, + vmName, ) ch <- prometheus.MustNewConstMetric( c.vmMemoryMemoryRemoveOperations, prometheus.CounterValue, data.VmMemoryMemoryRemoveOperations, - data.Name, + vmName, ) ch <- prometheus.MustNewConstMetric( c.vmMemoryMinimumPressure, prometheus.GaugeValue, utils.PercentageToRatio(data.VmMemoryMinimumPressure), - data.Name, + vmName, ) ch <- prometheus.MustNewConstMetric( c.vmMemoryPhysicalMemory, prometheus.GaugeValue, utils.MBToBytes(data.VmMemoryPhysicalMemory), - data.Name, + vmName, ) ch <- prometheus.MustNewConstMetric( c.vmMemoryRemovedMemory, prometheus.CounterValue, utils.MBToBytes(data.VmMemoryRemovedMemory), - data.Name, + vmName, ) } return nil } + +// vmUnknown is the vm label value for Hyper-V Dynamic Memory VM instances +// without a name. +const vmUnknown = "(unknown)" + +// dynamicMemoryVMName returns the vm label value for a Hyper-V Dynamic Memory VM +// instance. Windows reports an empty instance name for VMs managed by the Host +// Compute Service, e.g. WSL 2, which pdh exposes as +// pdh.InstanceEmpty. The occurrence suffix of duplicates is kept, so the +// series stay unique. +func dynamicMemoryVMName(name string) string { + if name == pdh.InstanceEmpty { + return vmUnknown + } + + if suffix, ok := strings.CutPrefix(name, pdh.InstanceEmpty+"#"); ok { + return vmUnknown + "#" + suffix + } + + return name +} diff --git a/internal/collector/hyperv/hyperv_dynamic_memory_vm_internal_test.go b/internal/collector/hyperv/hyperv_dynamic_memory_vm_internal_test.go new file mode 100644 index 00000000..929c895d --- /dev/null +++ b/internal/collector/hyperv/hyperv_dynamic_memory_vm_internal_test.go @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build windows + +package hyperv + +import ( + "testing" + + "github.com/prometheus-community/windows_exporter/internal/pdh" + "github.com/stretchr/testify/require" +) + +func TestDynamicMemoryVMName(t *testing.T) { + t.Parallel() + + for name, want := range map[string]string{ + pdh.InstanceEmpty: "(unknown)", + pdh.InstanceEmpty + "#1": "(unknown)#1", + pdh.InstanceEmpty + "#12": "(unknown)#12", + "GitHubActions": "GitHubActions", + "GitHubActions#1": "GitHubActions#1", + "vm-------": "vm-------", + } { + require.Equal(t, want, dynamicMemoryVMName(name), name) + } +}