diff --git a/CHANGELOG.md b/CHANGELOG.md index 09c3d5fbd9..2adfc3ae32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2631](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2631)) - `opentelemetry-instrumentation-system-metrics` Permit to use psutil 6.0+. ([#2630](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2630)) +- `opentelemetry-instrumentation-system-metrics` Add support for capture open file descriptors + ([#2652](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2652)) ### Breaking changes diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/__init__.py b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/__init__.py index 6342d287d5..b7ffb25431 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/__init__.py @@ -117,6 +117,7 @@ "process.runtime.thread_count": None, "process.runtime.cpu.utilization": None, "process.runtime.context_switches": ["involuntary", "voluntary"], + "process.open_file_descriptor.count": None, } if sys.platform == "darwin": @@ -169,6 +170,7 @@ def __init__( self._runtime_thread_count_labels = self._labels.copy() self._runtime_cpu_utilization_labels = self._labels.copy() self._runtime_context_switches_labels = self._labels.copy() + self._open_file_descriptor_count_labels = self._labels.copy() def instrumentation_dependencies(self) -> Collection[str]: return _instruments @@ -395,9 +397,25 @@ def _instrument(self, **kwargs): unit="switches", ) + if "process.open_file_descriptor.count" in self._config: + self._meter.create_observable_up_down_counter( + name="process.open_file_descriptor.count", + callbacks=[self._get_open_file_descriptors], + description="Number of file descriptors in use by the process.", + ) + def _uninstrument(self, **__): pass + def _get_open_file_descriptors( + self, options: CallbackOptions + ) -> Iterable[Observation]: + """Observer callback for Number of file descriptors in use by the process""" + yield Observation( + self._proc.num_fds(), + self._open_file_descriptor_count_labels.copy(), + ) + def _get_system_cpu_time( self, options: CallbackOptions ) -> Iterable[Observation]: diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py b/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py index 3986a32c16..1d6f08892e 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py @@ -118,12 +118,13 @@ def test_system_metrics_instrument(self): f"process.runtime.{self.implementation}.thread_count", f"process.runtime.{self.implementation}.context_switches", f"process.runtime.{self.implementation}.cpu.utilization", + "process.open_file_descriptor.count", ] if self.implementation == "pypy": - self.assertEqual(len(metric_names), 20) - else: self.assertEqual(len(metric_names), 21) + else: + self.assertEqual(len(metric_names), 22) observer_names.append( f"process.runtime.{self.implementation}.gc_count", ) @@ -842,3 +843,14 @@ def test_runtime_cpu_percent(self, mock_process_cpu_percent): self._test_metrics( f"process.runtime.{self.implementation}.cpu.utilization", expected ) + + @mock.patch("psutil.Process.num_fds") + def test_open_file_descriptor_count(self, mock_process_num_fds): + mock_process_num_fds.configure_mock(**{"return_value": 3}) + + expected = [_SystemMetricsResult({}, 3)] + self._test_metrics( + "process.open_file_descriptor.count", + expected, + ) + mock_process_num_fds.assert_called()