Skip to content

Commit 72f437c

Browse files
authored
opentelemetry-instrumentation-system-metrics: fix running on Google Cloud Run (#3533)
* opentelemetry-instrumentation-system-metrics: fix running on Google Cloud Run psutil fails reading context switches fails on Google Cloud Run, so before setting up the observable counter trying to read the values check we are actually being able to do so. * Please pylint
1 parent 9335366 commit 72f437c

File tree

3 files changed

+52
-4
lines changed

3 files changed

+52
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
### Fixed
1515

16+
- `opentelemetry-instrumentation-system-metrics`: fix loading on Google Cloud Run
17+
([#3533](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3533))
1618
- `opentelemetry-instrumentation-fastapi`: fix wrapping of middlewares
1719
([#3012](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3012))
1820
- `opentelemetry-instrumentation-urllib3`: proper bucket boundaries in stable semconv http duration metrics

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,10 @@ def _instrument(self, **kwargs: Any):
395395
self._meter, callbacks=[self._get_cpu_utilization]
396396
)
397397

398-
if "process.context_switches" in self._config:
398+
if (
399+
"process.context_switches" in self._config
400+
and self._can_read_context_switches()
401+
):
399402
self._meter.create_observable_counter(
400403
name="process.context_switches",
401404
callbacks=[self._get_context_switches],
@@ -482,7 +485,10 @@ def _instrument(self, **kwargs: Any):
482485
unit="1",
483486
)
484487

485-
if "process.runtime.context_switches" in self._config:
488+
if (
489+
"process.runtime.context_switches" in self._config
490+
and self._can_read_context_switches()
491+
):
486492
self._meter.create_observable_counter(
487493
name=f"process.runtime.{self._python_implementation}.context_switches",
488494
callbacks=[self._get_runtime_context_switches],
@@ -493,6 +499,14 @@ def _instrument(self, **kwargs: Any):
493499
def _uninstrument(self, **kwargs: Any):
494500
pass
495501

502+
def _can_read_context_switches(self) -> bool:
503+
"""On Google Cloud Run psutil is not able to read context switches, catch it before creating the observable instrument"""
504+
try:
505+
self._proc.num_ctx_switches()
506+
return True
507+
except NotImplementedError:
508+
return False
509+
496510
def _get_open_file_descriptors(
497511
self, options: CallbackOptions
498512
) -> Iterable[Observation]:

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

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
# pylint: disable=protected-access
15+
# pylint: disable=protected-access,too-many-lines
1616

1717
import sys
1818
from collections import namedtuple
@@ -235,14 +235,28 @@ def _assert_metrics(self, observer_name, reader, expected):
235235
assertions += 1
236236
self.assertEqual(len(expected), assertions)
237237

238-
def _test_metrics(self, observer_name, expected):
238+
@staticmethod
239+
def _setup_instrumentor() -> InMemoryMetricReader:
239240
reader = InMemoryMetricReader()
240241
meter_provider = MeterProvider(metric_readers=[reader])
241242

242243
system_metrics = SystemMetricsInstrumentor()
243244
system_metrics.instrument(meter_provider=meter_provider)
245+
return reader
246+
247+
def _test_metrics(self, observer_name, expected):
248+
reader = self._setup_instrumentor()
244249
self._assert_metrics(observer_name, reader, expected)
245250

251+
def _assert_metrics_not_found(self, observer_name):
252+
reader = self._setup_instrumentor()
253+
seen_metrics = set()
254+
for resource_metrics in reader.get_metrics_data().resource_metrics:
255+
for scope_metrics in resource_metrics.scope_metrics:
256+
for metric in scope_metrics.metrics:
257+
seen_metrics.add(metric.name)
258+
self.assertNotIn(observer_name, seen_metrics)
259+
246260
# This patch is added here to stop psutil from raising an exception
247261
# because we're patching cpu_times
248262
# pylint: disable=unused-argument
@@ -855,6 +869,14 @@ def test_context_switches(self, mock_process_num_ctx_switches):
855869
]
856870
self._test_metrics("process.context_switches", expected)
857871

872+
@mock.patch("psutil.Process.num_ctx_switches")
873+
def test_context_switches_not_implemented_error(
874+
self, mock_process_num_ctx_switches
875+
):
876+
mock_process_num_ctx_switches.side_effect = NotImplementedError
877+
878+
self._assert_metrics_not_found("process.context_switches")
879+
858880
@mock.patch("psutil.Process.num_threads")
859881
def test_thread_count(self, mock_process_thread_num):
860882
mock_process_thread_num.configure_mock(**{"return_value": 42})
@@ -947,6 +969,16 @@ def test_runtime_context_switches(self, mock_process_num_ctx_switches):
947969
f"process.runtime.{self.implementation}.context_switches", expected
948970
)
949971

972+
@mock.patch("psutil.Process.num_ctx_switches")
973+
def test_runtime_context_switches_not_implemented_error(
974+
self, mock_process_num_ctx_switches
975+
):
976+
mock_process_num_ctx_switches.side_effect = NotImplementedError
977+
978+
self._assert_metrics_not_found(
979+
f"process.runtime.{self.implementation}.context_switches",
980+
)
981+
950982
@mock.patch("psutil.Process.num_threads")
951983
def test_runtime_thread_count(self, mock_process_thread_num):
952984
mock_process_thread_num.configure_mock(**{"return_value": 42})

0 commit comments

Comments
 (0)