Skip to content

Commit 5535aa3

Browse files
pjanottiFiery-Fenix
authored andcommitted
[receiver/hostmetrics] Use native API instead of WMI to get process.handles on Windows (open-telemetry#38886)
Using the native [`GetProcessHandleCount`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocesshandlecount) Win32 API via `NumFDsWithContext` already implemented by `gopsutil` gives a noticeable performance improvement over the current version using WMI. * For non-Windows OSes the same behavior is kept for the metric `process.handles`: it returns a not supported platform error. * Corrected the documentation of `process.open_file_descriptors` with the actual behavior - as future breaking change we can consider return not supported for this metric on Windows. Expected performance improvement is shown on the image below. On the image the red line is the new implementation, the green the current one using WMI, and in blue is the WMI service handling the query made by the current implementation of the scraper. ![image](https://github.com/user-attachments/assets/4db79415-9e38-4c7f-a27c-99c24ffb24ed)
1 parent 742e6d8 commit 5535aa3

17 files changed

+130
-228
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: hostmetricsreceiver
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Reduced the CPU cost of collecting the `process.handles` metric on Windows.
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [38886]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext: |
19+
Instead of using WMI to retrieve the number of opened handles by each process
20+
the scraper now uses the GetProcessHandleCount Win32 API which results in
21+
reduced CPU usage when the metric `process.handles` is enabled.
22+
23+
# If your change doesn't affect end users or the exported elements of any package,
24+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
25+
# Optional: The change log or logs in which this entry should be included.
26+
# e.g. '[user]' or '[user, api]'
27+
# Include 'user' if the change is relevant to end users.
28+
# Include 'api' if there is a change to a library API.
29+
# Default: '[user]'
30+
change_logs: [user]

receiver/hostmetricsreceiver/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ require (
1313
github.com/shirou/gopsutil/v4 v4.25.2
1414
github.com/stretchr/testify v1.10.0
1515
github.com/tilinna/clock v1.1.0
16-
github.com/yusufpapurcu/wmi v1.2.4
1716
go.opentelemetry.io/collector/component v1.28.2-0.20250319144947-41a9ea7f7402
1817
go.opentelemetry.io/collector/component/componenttest v0.122.2-0.20250319144947-41a9ea7f7402
1918
go.opentelemetry.io/collector/confmap v1.28.2-0.20250319144947-41a9ea7f7402
@@ -95,6 +94,7 @@ require (
9594
github.com/testcontainers/testcontainers-go v0.35.0 // indirect
9695
github.com/tklauser/go-sysconf v0.3.12 // indirect
9796
github.com/tklauser/numcpus v0.6.1 // indirect
97+
github.com/yusufpapurcu/wmi v1.2.4 // indirect
9898
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
9999
go.opentelemetry.io/collector/consumer/consumererror v0.122.2-0.20250319144947-41a9ea7f7402 // indirect
100100
go.opentelemetry.io/collector/consumer/xconsumer v0.122.2-0.20250319144947-41a9ea7f7402 // indirect

receiver/hostmetricsreceiver/internal/scraper/processscraper/documentation.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ Number of disk operations performed by the process.
112112
113113
### process.handles
114114
115-
Number of handles held by the process.
115+
Number of open handles held by the process.
116116
117117
This metric is only available on Windows.
118118
@@ -132,7 +132,7 @@ Percentage of total physical memory that is used by the process.
132132
133133
Number of file descriptors in use by the process.
134134
135-
This metric is only available on Linux.
135+
On Windows this metric captures the number of open handles currently held by the process. If you want to capture this data on Windows use the `process.handles` metric instead to avoid any confusion.
136136

137137
| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic |
138138
| ---- | ----------- | ---------- | ----------------------- | --------- |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//go:build !windows
5+
6+
package processscraper // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/scraper/processscraper"
7+
8+
import (
9+
"context"
10+
"errors"
11+
)
12+
13+
const handleCountMetricsLen = 0
14+
15+
var ErrHandlesPlatformSupport = errors.New("process handle collection is only supported on Windows")
16+
17+
func (p *wrappedProcessHandle) GetProcessHandleCountWithContext(_ context.Context) (int64, error) {
18+
return 0, ErrHandlesPlatformSupport
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//go:build windows
5+
6+
package processscraper // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/scraper/processscraper"
7+
8+
import (
9+
"context"
10+
)
11+
12+
const handleCountMetricsLen = 1
13+
14+
func (p *wrappedProcessHandle) GetProcessHandleCountWithContext(ctx context.Context) (int64, error) {
15+
// On Windows NumFDsWithContext returns the number of open handles, since it uses the
16+
// GetProcessHandleCount API.
17+
fds, err := p.Process.NumFDsWithContext(ctx)
18+
return int64(fds), err
19+
}

receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/handlecount/handles.go

-9
This file was deleted.

receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/handlecount/handles_others.go

-24
This file was deleted.

receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/handlecount/handles_others_test.go

-22
This file was deleted.

receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/handlecount/handles_windows.go

-76
This file was deleted.

receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/handlecount/handles_windows_test.go

-50
This file was deleted.

receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/handlecount/package_test.go

-14
This file was deleted.

receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_metrics.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_metrics_test.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

receiver/hostmetricsreceiver/internal/scraper/processscraper/metadata.yaml

+5-2
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,10 @@ metrics:
170170
process.open_file_descriptors:
171171
enabled: false
172172
description: Number of file descriptors in use by the process.
173-
extended_documentation: This metric is only available on Linux.
173+
extended_documentation: >-
174+
On Windows this metric captures the number of open handles currently held
175+
by the process. If you want to capture this data on Windows use the
176+
`process.handles` metric instead to avoid any confusion.
174177
unit: '{count}'
175178
sum:
176179
value_type: int
@@ -179,7 +182,7 @@ metrics:
179182

180183
process.handles:
181184
enabled: false
182-
description: Number of handles held by the process.
185+
description: Number of open handles held by the process.
183186
extended_documentation: This metric is only available on Windows.
184187
unit: '{count}'
185188
sum:

receiver/hostmetricsreceiver/internal/scraper/processscraper/process.go

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ type processHandle interface {
9595
PageFaultsWithContext(context.Context) (*process.PageFaultsStat, error)
9696
NumCtxSwitchesWithContext(context.Context) (*process.NumCtxSwitchesStat, error)
9797
NumFDsWithContext(context.Context) (int32, error)
98+
GetProcessHandleCountWithContext(context.Context) (int64, error)
9899
// If gatherUsed is true, the currently used value will be gathered and added to the resulting RlimitStat.
99100
RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]process.RlimitStat, error)
100101
CgroupWithContext(ctx context.Context) (string, error)

0 commit comments

Comments
 (0)