Skip to content

Commit 4447323

Browse files
authored
Add support for process.disk.io metric in system-metrics instrumentation (#4397)
1 parent 921da8b commit 4447323

3 files changed

Lines changed: 59 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- Add `BaggageLogProcessor` to `opentelemetry-processor-baggage`
1616
([#4371](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4371))
1717

18+
- `opentelemetry-instrumentation-system-metrics`: Add support for `process.disk.io` metric in system-metrics instrumentation
19+
([#4397](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/4397))
1820
- Switch to SPDX license headers and add CI enforcement
1921
([#4533](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4533))
2022
- Bump `pylint` to `4.0.5`

instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/__init__.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"process.cpu.utilization": None,
3131
"process.memory.usage": None,
3232
"process.memory.virtual": None,
33+
"process.disk.io": ["read", "write"],
3334
"process.open_file_descriptor.count": None,
3435
"process.thread.count": None,
3536
"process.runtime.memory": ["rss", "vms"],
@@ -134,6 +135,7 @@
134135
"process.memory.virtual": None,
135136
"process.open_file_descriptor.count": None,
136137
"process.thread.count": None,
138+
"process.disk.io": ["read", "write"],
137139
"process.runtime.memory": ["rss", "vms"],
138140
"process.runtime.cpu.time": ["user", "system"],
139141
"process.runtime.gc_count": None,
@@ -208,6 +210,7 @@ def __init__(
208210
self._cpu_utilization_labels = self._labels.copy()
209211
self._memory_usage_labels = self._labels.copy()
210212
self._memory_virtual_labels = self._labels.copy()
213+
self._process_disk_io_labels = self._labels.copy()
211214
self._open_file_descriptor_count_labels = self._labels.copy()
212215
self._thread_count_labels = self._labels.copy()
213216

@@ -459,6 +462,14 @@ def _instrument(self, **kwargs: Any):
459462
description="Process threads count.",
460463
)
461464

465+
if "process.disk.io" in self._config:
466+
self._meter.create_observable_counter(
467+
name="process.disk.io",
468+
callbacks=[self._get_process_disk_io],
469+
description="Disk bytes transferred for the process.",
470+
unit="By",
471+
)
472+
462473
# FIXME: process.runtime keys are deprecated and will be removed in subsequent releases.
463474
# When removing them, remember to clean also the callbacks and labels
464475

@@ -909,6 +920,27 @@ def _get_thread_count(
909920
self._proc.num_threads(), self._thread_count_labels.copy()
910921
)
911922

923+
def _get_process_disk_io(
924+
self, options: CallbackOptions
925+
) -> Iterable[Observation]:
926+
"""Observer callback for process disk IO"""
927+
try:
928+
proc_disk = self._proc.io_counters()
929+
except (AttributeError, NotImplementedError, PermissionError):
930+
# io_counters() is not available on all platforms (e.g. macOS)
931+
return
932+
for metric in self._config["process.disk.io"]:
933+
if metric == "read":
934+
self._process_disk_io_labels["direction"] = "read"
935+
yield Observation(
936+
proc_disk.read_bytes, self._process_disk_io_labels.copy()
937+
)
938+
elif metric == "write":
939+
self._process_disk_io_labels["direction"] = "write"
940+
yield Observation(
941+
proc_disk.write_bytes, self._process_disk_io_labels.copy()
942+
)
943+
912944
# runtime callbacks
913945

914946
def _get_runtime_memory(

instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ def test_system_metrics_instrument(self):
110110
"process.cpu.time",
111111
"process.cpu.utilization",
112112
"process.memory.usage",
113+
"process.disk.io",
113114
"process.memory.virtual",
114115
"process.thread.count",
115116
f"process.runtime.{self.implementation}.memory",
@@ -151,6 +152,7 @@ def test_process_metrics_instrument(self):
151152
"process.memory.virtual": None,
152153
"process.open_file_descriptor.count": None,
153154
"process.thread.count": None,
155+
"process.disk.io": ["read", "write"],
154156
}
155157

156158
reader = InMemoryMetricReader()
@@ -169,6 +171,7 @@ def test_process_metrics_instrument(self):
169171
"process.memory.virtual",
170172
"process.cpu.time",
171173
"process.thread.count",
174+
"process.disk.io",
172175
"process.context_switches",
173176
"process.cpu.utilization",
174177
]
@@ -887,6 +890,28 @@ def test_thread_count(self, mock_process_thread_num):
887890
expected = [_SystemMetricsResult({}, 42)]
888891
self._test_metrics("process.thread.count", expected)
889892

893+
@mock.patch("psutil.Process.io_counters")
894+
def test_process_disk_io(self, mock_process_io_counters):
895+
PIOCounters = namedtuple("PIOCounters", ["read_bytes", "write_bytes"])
896+
897+
mock_process_io_counters.configure_mock(
898+
**{"return_value": PIOCounters(read_bytes=1024, write_bytes=2048)}
899+
)
900+
901+
expected = [
902+
_SystemMetricsResult({"direction": "read"}, 1024),
903+
_SystemMetricsResult({"direction": "write"}, 2048),
904+
]
905+
self._test_metrics("process.disk.io", expected)
906+
907+
@mock.patch("psutil.Process.io_counters")
908+
def test_process_disk_io_not_implemented_error(
909+
self, mock_process_io_counters
910+
):
911+
mock_process_io_counters.side_effect = NotImplementedError
912+
913+
self._assert_metrics_not_found("process.disk.io")
914+
890915
@mock.patch("psutil.Process.cpu_percent")
891916
@mock.patch("psutil.cpu_count")
892917
def test_cpu_utilization(self, mock_cpu_count, mock_process_cpu_percent):

0 commit comments

Comments
 (0)