Skip to content

Commit 8a51981

Browse files
committed
add gc_count metrics with correct collection unit .
fixes #3549
1 parent 77f3171 commit 8a51981

File tree

3 files changed

+49
-2
lines changed

3 files changed

+49
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2222
([#3610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3610))
2323
- `opentelemetry-instrumentation-kafka-python` Utilize instruments-any functionality.
2424
([#3610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3610))
25+
- `opentelemetry-instrumentation-system-metrics`: Add `cpython.gc.collections` metrics with collection unit is specified in semconv ([3617](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3617))
2526

2627
## Version 1.35.0/0.56b0 (2025-07-11)
2728

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"process.runtime.memory": ["rss", "vms"],
4545
"process.runtime.cpu.time": ["user", "system"],
4646
"process.runtime.gc_count": None,
47+
"cpython.gc.collections": None,
4748
"process.runtime.thread_count": None,
4849
"process.runtime.cpu.utilization": None,
4950
"process.runtime.context_switches": ["involuntary", "voluntary"],
@@ -136,6 +137,7 @@
136137
"process.runtime.memory": ["rss", "vms"],
137138
"process.runtime.cpu.time": ["user", "system"],
138139
"process.runtime.gc_count": None,
140+
"cpython.gc.collections": None,
139141
"process.runtime.thread_count": None,
140142
"process.runtime.cpu.utilization": None,
141143
"process.runtime.context_switches": ["involuntary", "voluntary"],
@@ -196,6 +198,7 @@ def __init__(
196198
self._runtime_memory_labels = self._labels.copy()
197199
self._runtime_cpu_time_labels = self._labels.copy()
198200
self._runtime_gc_count_labels = self._labels.copy()
201+
self._runtime_gc_collections_labels = self._labels.copy()
199202
self._runtime_thread_count_labels = self._labels.copy()
200203
self._runtime_cpu_utilization_labels = self._labels.copy()
201204
self._runtime_context_switches_labels = self._labels.copy()
@@ -470,6 +473,19 @@ def _instrument(self, **kwargs: Any):
470473
unit="By",
471474
)
472475

476+
if "cpython.gc.collections" in self._config:
477+
if self._python_implementation == "pypy":
478+
_logger.warning(
479+
"The cpython.gc.collections metric won't be collected because the interpreter is PyPy"
480+
)
481+
else:
482+
self._meter.create_observable_counter(
483+
name="cpython.gc.collections",
484+
callbacks=[self._get_runtime_gc_collections],
485+
description="The number of times a generation was collected since interpreter start.",
486+
unit="{collection}",
487+
)
488+
473489
if "process.runtime.thread_count" in self._config:
474490
self._meter.create_observable_up_down_counter(
475491
name=f"process.runtime.{self._python_implementation}.thread_count",
@@ -885,6 +901,16 @@ def _get_runtime_gc_count(
885901
self._runtime_gc_count_labels["count"] = str(index)
886902
yield Observation(count, self._runtime_gc_count_labels.copy())
887903

904+
def _get_runtime_gc_collections(
905+
self, options: CallbackOptions
906+
) -> Iterable[Observation]:
907+
"""Observer callback for garbage collection"""
908+
for index, count in enumerate(gc.get_count()):
909+
self._runtime_gc_collections_labels["generation"] = str(index)
910+
yield Observation(
911+
count, self._runtime_gc_collections_labels.copy()
912+
)
913+
888914
def _get_runtime_thread_count(
889915
self, options: CallbackOptions
890916
) -> Iterable[Observation]:

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ def test_system_metrics_instrument(self):
136136
observer_names.append(
137137
f"process.runtime.{self.implementation}.gc_count",
138138
)
139+
observer_names.append(
140+
"cpython.gc.collections",
141+
)
139142
if sys.platform != "darwin":
140143
observer_names.append("system.network.connections")
141144

@@ -946,13 +949,30 @@ def test_runtime_cpu_time(self, mock_process_cpu_times):
946949
def test_runtime_get_count(self, mock_gc_get_count):
947950
mock_gc_get_count.configure_mock(**{"return_value": (1, 2, 3)})
948951

949-
expected = [
952+
expected_gc_count = [
950953
_SystemMetricsResult({"count": "0"}, 1),
951954
_SystemMetricsResult({"count": "1"}, 2),
952955
_SystemMetricsResult({"count": "2"}, 3),
953956
]
954957
self._test_metrics(
955-
f"process.runtime.{self.implementation}.gc_count", expected
958+
f"process.runtime.{self.implementation}.gc_count",
959+
expected_gc_count,
960+
)
961+
962+
@mock.patch("gc.get_count")
963+
@skipIf(
964+
python_implementation().lower() == "pypy", "not supported for pypy"
965+
)
966+
def test_runtime_get_gc_collections(self, mock_gc_get_count):
967+
mock_gc_get_count.configure_mock(**{"return_value": (1, 2, 3)})
968+
expected_gc_collections = [
969+
_SystemMetricsResult({"generation": "0"}, 1),
970+
_SystemMetricsResult({"generation": "1"}, 2),
971+
_SystemMetricsResult({"generation": "2"}, 3),
972+
]
973+
self._test_metrics(
974+
"cpython.gc.collections",
975+
expected_gc_collections,
956976
)
957977

958978
@mock.patch("psutil.Process.num_ctx_switches")

0 commit comments

Comments
 (0)