diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dfee88d7b..c07bbb7444 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.13.0-0.34b0...HEAD) +### Added + +- `opentelemetry-instrumentation-system-metrics` add supports to collect system thread count. ([#1339](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1339)) + ## [1.13.0-0.34b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.13.0-0.34b0) - 2022-09-26 @@ -26,14 +30,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#1253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1253)) - Add metric instrumentation in starlette ([#1327](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1327)) - + ### Fixed - `opentelemetry-instrumentation-boto3sqs` Make propagation compatible with other SQS instrumentations, add 'messaging.url' span attribute, and fix missing package dependencies. - ([#1234](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1234)) + ([#1234](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1234)) - `opentelemetry-instrumentation-pymongo` Change span names to not contain queries but only database name and command name - ([#1247](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1247)) + ([#1247](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1247)) - restoring metrics in django framework ([#1208](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1208)) - `opentelemetry-instrumentation-aiohttp-client` Fix producing additional spans with each newly created ClientSession 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 72f20ba3d1..420bad5d66 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 @@ -33,6 +33,7 @@ "system.network.errors": ["transmit", "receive"], "system.network.io": ["transmit", "receive"], "system.network.connections": ["family", "type"], + "system.thread_count": None "runtime.memory": ["rss", "vms"], "runtime.cpu.time": ["user", "system"], } @@ -71,6 +72,7 @@ import gc import os +import threading from platform import python_implementation from typing import Collection, Dict, Iterable, List, Optional @@ -99,6 +101,7 @@ "system.network.errors": ["transmit", "receive"], "system.network.io": ["transmit", "receive"], "system.network.connections": ["family", "type"], + "system.thread_count": None, "runtime.memory": ["rss", "vms"], "runtime.cpu.time": ["user", "system"], "runtime.gc_count": None, @@ -142,6 +145,8 @@ def __init__( self._system_network_io_labels = self._labels.copy() self._system_network_connections_labels = self._labels.copy() + self._system_thread_count_labels = self._labels.copy() + self._runtime_memory_labels = self._labels.copy() self._runtime_cpu_time_labels = self._labels.copy() self._runtime_gc_count_labels = self._labels.copy() @@ -311,6 +316,13 @@ def _instrument(self, **kwargs): unit="connections", ) + if "system.thread_count" in self._config: + self._meter.create_observable_gauge( + name="system.thread_count", + callbacks=[self._get_system_thread_count], + description="System active threads count", + ) + if "runtime.memory" in self._config: self._meter.create_observable_counter( name=f"runtime.{self._python_implementation}.memory", @@ -593,6 +605,14 @@ def _get_system_network_connections( connection_counter["labels"], ) + def _get_system_thread_count( + self, options: CallbackOptions + ) -> Iterable[Observation]: + """Observer callback for active thread count""" + yield Observation( + threading.active_count(), self._system_thread_count_labels + ) + def _get_runtime_memory( 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 04d7674dba..aadc834301 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py @@ -51,6 +51,7 @@ def __init__(self, attributes, value) -> None: self.value = value +# pylint:disable=too-many-public-methods class TestSystemMetrics(TestBase): def setUp(self): super().setUp() @@ -75,7 +76,7 @@ def test_system_metrics_instrument(self): for scope_metrics in resource_metrics.scope_metrics: for metric in scope_metrics.metrics: metric_names.append(metric.name) - self.assertEqual(len(metric_names), 17) + self.assertEqual(len(metric_names), 18) observer_names = [ "system.cpu.time", @@ -92,6 +93,7 @@ def test_system_metrics_instrument(self): "system.network.errors", "system.network.io", "system.network.connections", + "system.thread_count", f"runtime.{self.implementation}.memory", f"runtime.{self.implementation}.cpu_time", f"runtime.{self.implementation}.gc_count", @@ -680,6 +682,13 @@ def test_system_network_connections(self, mock_net_connections): ] self._test_metrics("system.network.connections", expected) + @mock.patch("threading.active_count") + def test_system_thread_count(self, threading_active_count): + threading_active_count.return_value = 42 + + expected = [_SystemMetricsResult({}, 42)] + self._test_metrics("system.thread_count", expected) + @mock.patch("psutil.Process.memory_info") def test_runtime_memory(self, mock_process_memory_info):