From 703fa437741b55143d58370b7cd9c062e4d1b640 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Tue, 4 Aug 2020 19:10:51 -0700 Subject: [PATCH 1/9] Rename remaining framework packages from "ext" to "instrumentation" (#969) --- .../CHANGELOG.md | 12 + .../LICENSE | 201 +++++++++++++++ .../MANIFEST.in | 9 + .../README.rst | 24 ++ .../setup.cfg | 51 ++++ .../setup.py | 31 +++ .../system_metrics/__init__.py | 235 ++++++++++++++++++ .../instrumentation/system_metrics/version.py | 15 ++ .../tests/__init__.py | 0 .../tests/test_system_metrics.py | 203 +++++++++++++++ 10 files changed, 781 insertions(+) create mode 100644 instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md create mode 100644 instrumentation/opentelemetry-instrumentation-system-metrics/LICENSE create mode 100644 instrumentation/opentelemetry-instrumentation-system-metrics/MANIFEST.in create mode 100644 instrumentation/opentelemetry-instrumentation-system-metrics/README.rst create mode 100644 instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg create mode 100644 instrumentation/opentelemetry-instrumentation-system-metrics/setup.py create mode 100644 instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/__init__.py create mode 100644 instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py create mode 100644 instrumentation/opentelemetry-instrumentation-system-metrics/tests/__init__.py create mode 100644 instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md new file mode 100644 index 000000000..f752d8314 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md @@ -0,0 +1,12 @@ +# Changelog + +## Unreleased + +- Change package name to opentelemetry-instrumentation-system-metrics + ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) + +## 0.9b0 + +Released 2020-06-10 + +- Initial release (https://github.com/open-telemetry/opentelemetry-python/pull/652) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/LICENSE b/instrumentation/opentelemetry-instrumentation-system-metrics/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-system-metrics/MANIFEST.in new file mode 100644 index 000000000..aed3e3327 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/MANIFEST.in @@ -0,0 +1,9 @@ +graft src +graft tests +global-exclude *.pyc +global-exclude *.pyo +global-exclude __pycache__/* +include CHANGELOG.md +include MANIFEST.in +include README.rst +include LICENSE diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/README.rst b/instrumentation/opentelemetry-instrumentation-system-metrics/README.rst new file mode 100644 index 000000000..fc984256b --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/README.rst @@ -0,0 +1,24 @@ +OpenTelemetry System Metrics Instrumentation +============================================ + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-system-metrics.svg + :target: https://pypi.org/project/opentelemetry-instrumentation-system-metrics/ + +Instrumentation to collect system performance metrics. + + +Installation +------------ + +:: + + pip install opentelemetry-instrumentation-system-metrics + + +References +---------- +* `OpenTelemetry System Metrics Instrumentation `_ +* `OpenTelemetry Project `_ + diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg new file mode 100644 index 000000000..4b6f4f07f --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg @@ -0,0 +1,51 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +[metadata] +name = opentelemetry-instrumentation-system-metrics +description = OpenTelemetry System Metrics Instrumentation +long_description = file: README.rst +long_description_content_type = text/x-rst +author = OpenTelemetry Authors +author_email = cncf-opentelemetry-contributors@lists.cncf.io +url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-system-metrics +platforms = any +license = Apache-2.0 +classifiers = + Development Status :: 4 - Beta + Intended Audience :: Developers + License :: OSI Approved :: Apache Software License + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.4 + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + +[options] +python_requires = >=3.4 +package_dir= + =src +packages=find_namespace: +install_requires = + opentelemetry-api == 0.12.dev0 + psutil ~= 5.7.0 + +[options.extras_require] +test = + opentelemetry-test == 0.12.dev0 + +[options.packages.find] +where = src diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.py b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.py new file mode 100644 index 000000000..f0bbf9eff --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.py @@ -0,0 +1,31 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os + +import setuptools + +BASE_DIR = os.path.dirname(__file__) +VERSION_FILENAME = os.path.join( + BASE_DIR, + "src", + "opentelemetry", + "instrumentation", + "system_metrics", + "version.py", +) +PACKAGE_INFO = {} +with open(VERSION_FILENAME) as f: + exec(f.read(), PACKAGE_INFO) + +setuptools.setup(version=PACKAGE_INFO["__version__"]) 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 new file mode 100644 index 000000000..fcd96f821 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/__init__.py @@ -0,0 +1,235 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Instrument to report system (CPU, memory, network) and +process (CPU, memory, garbage collection) metrics. By default, the +following metrics are configured: + +"system_memory": ["total", "available", "used", "free"], +"system_cpu": ["user", "system", "idle"], +"network_bytes": ["bytes_recv", "bytes_sent"], +"runtime_memory": ["rss", "vms"], +"runtime_cpu": ["user", "system"], + + +Usage +----- + +.. code:: python + + from opentelemetry import metrics + from opentelemetry.instrumentation.system_metrics import SystemMetrics + from opentelemetry.sdk.metrics import MeterProvider, + from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter + + metrics.set_meter_provider(MeterProvider()) + exporter = ConsoleMetricsExporter() + SystemMetrics(exporter) + + # metrics are collected asynchronously + input("...") + + # to configure custom metrics + configuration = { + "system_memory": ["total", "available", "used", "free", "active", "inactive", "wired"], + "system_cpu": ["user", "nice", "system", "idle"], + "network_bytes": ["bytes_recv", "bytes_sent"], + "runtime_memory": ["rss", "vms"], + "runtime_cpu": ["user", "system"], + } + SystemMetrics(exporter, config=configuration) + +API +--- +""" + +import gc +import os +import typing + +import psutil + +from opentelemetry import metrics +from opentelemetry.sdk.metrics import ValueObserver +from opentelemetry.sdk.metrics.export import MetricsExporter +from opentelemetry.sdk.metrics.export.controller import PushController + + +class SystemMetrics: + def __init__( + self, + exporter: MetricsExporter, + interval: int = 30, + labels: typing.Optional[typing.Dict[str, str]] = None, + config: typing.Optional[typing.Dict[str, typing.List[str]]] = None, + ): + self._labels = {} if labels is None else labels + self.meter = metrics.get_meter(__name__) + self.controller = PushController( + meter=self.meter, exporter=exporter, interval=interval + ) + if config is None: + self._config = { + "system_memory": ["total", "available", "used", "free"], + "system_cpu": ["user", "system", "idle"], + "network_bytes": ["bytes_recv", "bytes_sent"], + "runtime_memory": ["rss", "vms"], + "runtime_cpu": ["user", "system"], + } + else: + self._config = config + self._proc = psutil.Process(os.getpid()) + self._system_memory_labels = {} + self._system_cpu_labels = {} + self._network_bytes_labels = {} + self._runtime_memory_labels = {} + self._runtime_cpu_labels = {} + self._runtime_gc_labels = {} + # create the label set for each observer once + for key, value in self._labels.items(): + self._system_memory_labels[key] = value + self._system_cpu_labels[key] = value + self._network_bytes_labels[key] = value + self._runtime_memory_labels[key] = value + self._runtime_gc_labels[key] = value + + self.meter.register_observer( + callback=self._get_system_memory, + name="system.mem", + description="System memory", + unit="bytes", + value_type=int, + observer_type=ValueObserver, + ) + + self.meter.register_observer( + callback=self._get_system_cpu, + name="system.cpu", + description="System CPU", + unit="seconds", + value_type=float, + observer_type=ValueObserver, + ) + + self.meter.register_observer( + callback=self._get_network_bytes, + name="system.net.bytes", + description="System network bytes", + unit="bytes", + value_type=int, + observer_type=ValueObserver, + ) + + self.meter.register_observer( + callback=self._get_runtime_memory, + name="runtime.python.mem", + description="Runtime memory", + unit="bytes", + value_type=int, + observer_type=ValueObserver, + ) + + self.meter.register_observer( + callback=self._get_runtime_cpu, + name="runtime.python.cpu", + description="Runtime CPU", + unit="seconds", + value_type=float, + observer_type=ValueObserver, + ) + + self.meter.register_observer( + callback=self._get_runtime_gc_count, + name="runtime.python.gc.count", + description="Runtime: gc objects", + unit="objects", + value_type=int, + observer_type=ValueObserver, + ) + + def _get_system_memory(self, observer: metrics.ValueObserver) -> None: + """Observer callback for memory available + + Args: + observer: the observer to update + """ + system_memory = psutil.virtual_memory() + for metric in self._config["system_memory"]: + self._system_memory_labels["type"] = metric + observer.observe( + getattr(system_memory, metric), self._system_memory_labels + ) + + def _get_system_cpu(self, observer: metrics.ValueObserver) -> None: + """Observer callback for system cpu + + Args: + observer: the observer to update + """ + cpu_times = psutil.cpu_times() + for _type in self._config["system_cpu"]: + self._system_cpu_labels["type"] = _type + observer.observe( + getattr(cpu_times, _type), self._system_cpu_labels + ) + + def _get_network_bytes(self, observer: metrics.ValueObserver) -> None: + """Observer callback for network bytes + + Args: + observer: the observer to update + """ + net_io = psutil.net_io_counters() + for _type in self._config["network_bytes"]: + self._network_bytes_labels["type"] = _type + observer.observe( + getattr(net_io, _type), self._network_bytes_labels + ) + + def _get_runtime_memory(self, observer: metrics.ValueObserver) -> None: + """Observer callback for runtime memory + + Args: + observer: the observer to update + """ + proc_memory = self._proc.memory_info() + for _type in self._config["runtime_memory"]: + self._runtime_memory_labels["type"] = _type + observer.observe( + getattr(proc_memory, _type), self._runtime_memory_labels + ) + + def _get_runtime_cpu(self, observer: metrics.ValueObserver) -> None: + """Observer callback for runtime CPU + + Args: + observer: the observer to update + """ + proc_cpu = self._proc.cpu_times() + for _type in self._config["runtime_cpu"]: + self._runtime_cpu_labels["type"] = _type + observer.observe( + getattr(proc_cpu, _type), self._runtime_cpu_labels + ) + + def _get_runtime_gc_count(self, observer: metrics.ValueObserver) -> None: + """Observer callback for garbage collection + + Args: + observer: the observer to update + """ + gc_count = gc.get_count() + for index, count in enumerate(gc_count): + self._runtime_gc_labels["count"] = str(index) + observer.observe(count, self._runtime_gc_labels) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py new file mode 100644 index 000000000..780a92b6a --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py @@ -0,0 +1,15 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +__version__ = "0.12.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-system-metrics/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py b/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py new file mode 100644 index 000000000..b9ae662af --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py @@ -0,0 +1,203 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections import namedtuple +from unittest import mock + +from opentelemetry import metrics +from opentelemetry.instrumentation.system_metrics import SystemMetrics +from opentelemetry.test.test_base import TestBase + + +class TestSystemMetrics(TestBase): + def setUp(self): + super().setUp() + self.memory_metrics_exporter.clear() + + def test_system_metrics_constructor(self): + # ensure the observers have been registered + meter = metrics.get_meter(__name__) + with mock.patch("opentelemetry.metrics.get_meter") as mock_get_meter: + mock_get_meter.return_value = meter + SystemMetrics(self.memory_metrics_exporter) + self.assertEqual(len(meter.observers), 6) + observer_names = [ + "system.mem", + "system.cpu", + "system.net.bytes", + "runtime.python.mem", + "runtime.python.cpu", + "runtime.python.gc.count", + ] + for observer in meter.observers: + self.assertIn(observer.name, observer_names) + observer_names.remove(observer.name) + + def _assert_metrics(self, observer_name, system_metrics, expected): + system_metrics.controller.tick() + assertions = 0 + for ( + metric + ) in ( + self.memory_metrics_exporter._exported_metrics # pylint: disable=protected-access + ): + if ( + metric.labels in expected + and metric.instrument.name == observer_name + ): + self.assertEqual( + metric.aggregator.checkpoint.last, expected[metric.labels], + ) + assertions += 1 + self.assertEqual(len(expected), assertions) + + def _test_metrics(self, observer_name, expected): + meter = self.meter_provider.get_meter(__name__) + + with mock.patch("opentelemetry.metrics.get_meter") as mock_get_meter: + mock_get_meter.return_value = meter + system_metrics = SystemMetrics(self.memory_metrics_exporter) + self._assert_metrics(observer_name, system_metrics, expected) + + @mock.patch("psutil.cpu_times") + def test_system_cpu(self, mock_cpu_times): + CPUTimes = namedtuple("CPUTimes", ["user", "nice", "system", "idle"]) + mock_cpu_times.return_value = CPUTimes( + user=332277.48, nice=0.0, system=309836.43, idle=6724698.94 + ) + + expected = { + (("type", "user"),): 332277.48, + (("type", "system"),): 309836.43, + (("type", "idle"),): 6724698.94, + } + self._test_metrics("system.cpu", expected) + + @mock.patch("psutil.virtual_memory") + def test_system_memory(self, mock_virtual_memory): + VirtualMemory = namedtuple( + "VirtualMemory", + [ + "total", + "available", + "percent", + "used", + "free", + "active", + "inactive", + "wired", + ], + ) + mock_virtual_memory.return_value = VirtualMemory( + total=17179869184, + available=5520928768, + percent=67.9, + used=10263990272, + free=266964992, + active=5282459648, + inactive=5148700672, + wired=4981530624, + ) + + expected = { + (("type", "total"),): 17179869184, + (("type", "used"),): 10263990272, + (("type", "available"),): 5520928768, + (("type", "free"),): 266964992, + } + self._test_metrics("system.mem", expected) + + @mock.patch("psutil.net_io_counters") + def test_network_bytes(self, mock_net_io_counters): + NetworkIO = namedtuple( + "NetworkIO", + ["bytes_sent", "bytes_recv", "packets_recv", "packets_sent"], + ) + mock_net_io_counters.return_value = NetworkIO( + bytes_sent=23920188416, + bytes_recv=46798894080, + packets_sent=53127118, + packets_recv=53205738, + ) + + expected = { + (("type", "bytes_recv"),): 46798894080, + (("type", "bytes_sent"),): 23920188416, + } + self._test_metrics("system.net.bytes", expected) + + def test_runtime_memory(self): + meter = self.meter_provider.get_meter(__name__) + with mock.patch("opentelemetry.metrics.get_meter") as mock_get_meter: + mock_get_meter.return_value = meter + system_metrics = SystemMetrics(self.memory_metrics_exporter) + + with mock.patch.object( + system_metrics._proc, # pylint: disable=protected-access + "memory_info", + ) as mock_runtime_memory: + RuntimeMemory = namedtuple( + "RuntimeMemory", ["rss", "vms", "pfaults", "pageins"], + ) + mock_runtime_memory.return_value = RuntimeMemory( + rss=9777152, vms=4385665024, pfaults=2631, pageins=49 + ) + expected = { + (("type", "rss"),): 9777152, + (("type", "vms"),): 4385665024, + } + self._assert_metrics( + "runtime.python.mem", system_metrics, expected + ) + + def test_runtime_cpu(self): + meter = self.meter_provider.get_meter(__name__) + with mock.patch("opentelemetry.metrics.get_meter") as mock_get_meter: + mock_get_meter.return_value = meter + system_metrics = SystemMetrics(self.memory_metrics_exporter) + + with mock.patch.object( + system_metrics._proc, # pylint: disable=protected-access + "cpu_times", + ) as mock_runtime_cpu_times: + RuntimeCPU = namedtuple( + "RuntimeCPU", ["user", "nice", "system"] + ) + mock_runtime_cpu_times.return_value = RuntimeCPU( + user=100.48, nice=0.0, system=200.43 + ) + + expected = { + (("type", "user"),): 100.48, + (("type", "system"),): 200.43, + } + + self._assert_metrics( + "runtime.python.cpu", system_metrics, expected + ) + + @mock.patch("gc.get_count") + def test_runtime_gc_count(self, mock_gc): + mock_gc.return_value = [ + 100, # gen0 + 50, # gen1 + 10, # gen2 + ] + + expected = { + (("count", "0"),): 100, + (("count", "1"),): 50, + (("count", "2"),): 10, + } + self._test_metrics("runtime.python.gc.count", expected) From bfd80edbe30b3e8564f8a302f96c701d261e7db9 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Sat, 15 Aug 2020 18:06:27 -0700 Subject: [PATCH 2/9] chore: 0.13.dev0 version update (#991) --- .../opentelemetry-instrumentation-system-metrics/CHANGELOG.md | 4 ++++ .../opentelemetry-instrumentation-system-metrics/setup.cfg | 4 ++-- .../opentelemetry/instrumentation/system_metrics/version.py | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md index f752d8314..4bd1389cc 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## Version 0.12b0 + +Released 2020-08-14 + - Change package name to opentelemetry-instrumentation-system-metrics ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg index 4b6f4f07f..4a93873ce 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg @@ -40,12 +40,12 @@ package_dir= =src packages=find_namespace: install_requires = - opentelemetry-api == 0.12.dev0 + opentelemetry-api == 0.13dev0 psutil ~= 5.7.0 [options.extras_require] test = - opentelemetry-test == 0.12.dev0 + opentelemetry-test == 0.13dev0 [options.packages.find] where = src diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py index 780a92b6a..9cc445d09 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.12.dev0" +__version__ = "0.13dev0" From 5ddecb4b8ee62f4adb358a3a84c8aad59a8033af Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 3 Sep 2020 09:07:03 -0700 Subject: [PATCH 3/9] Attributes returned from SamplingResult must be immutable (#1066) --- .../opentelemetry-instrumentation-system-metrics/setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg index 4a93873ce..70c784c60 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg @@ -41,6 +41,7 @@ package_dir= packages=find_namespace: install_requires = opentelemetry-api == 0.13dev0 + opentelemetry-sdk == 0.13dev0 psutil ~= 5.7.0 [options.extras_require] From 80f73e675fc07182df6892ed292e4d79ba4dc388 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 3 Sep 2020 18:19:13 -0600 Subject: [PATCH 4/9] instrumentation/system-metrics: Update System Metrics (#1019) Fixes #1006 --- .../system_metrics/__init__.py | 624 ++++++++++++-- .../tests/test_system_metrics.py | 768 +++++++++++++++--- 2 files changed, 1174 insertions(+), 218 deletions(-) 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 fcd96f821..9ca36d00b 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 @@ -16,12 +16,27 @@ Instrument to report system (CPU, memory, network) and process (CPU, memory, garbage collection) metrics. By default, the following metrics are configured: -"system_memory": ["total", "available", "used", "free"], -"system_cpu": ["user", "system", "idle"], -"network_bytes": ["bytes_recv", "bytes_sent"], -"runtime_memory": ["rss", "vms"], -"runtime_cpu": ["user", "system"], +.. code:: python + { + "system.cpu.time": ["idle", "user", "system", "irq"], + "system.cpu.utilization": ["idle", "user", "system", "irq"], + "system.memory.usage": ["used", "free", "cached"], + "system.memory.utilization": ["used", "free", "cached"], + "system.swap.usage": ["used", "free"], + "system.swap.utilization": ["used", "free"], + "system.disk.io": ["read", "write"], + "system.disk.operations": ["read", "write"], + "system.disk.time": ["read", "write"], + "system.disk.merged": ["read", "write"], + "system.network.dropped.packets": ["transmit", "receive"], + "system.network.packets": ["transmit", "receive"], + "system.network.errors": ["transmit", "receive"], + "system.network.io": ["trasmit", "receive"], + "system.network.connections": ["family", "type"], + "runtime.memory": ["rss", "vms"], + "runtime.cpu.time": ["user", "system"], + } Usage ----- @@ -42,11 +57,11 @@ Usage # to configure custom metrics configuration = { - "system_memory": ["total", "available", "used", "free", "active", "inactive", "wired"], - "system_cpu": ["user", "nice", "system", "idle"], - "network_bytes": ["bytes_recv", "bytes_sent"], - "runtime_memory": ["rss", "vms"], - "runtime_cpu": ["user", "system"], + "system.memory.usage": ["used", "free", "cached"], + "system.cpu.time": ["idle", "user", "system", "irq"], + "system.network.io": ["trasmit", "receive"], + "runtime.memory": ["rss", "vms"], + "runtime.cpu.time": ["user", "system"], } SystemMetrics(exporter, config=configuration) @@ -57,16 +72,23 @@ API import gc import os import typing +from platform import python_implementation import psutil from opentelemetry import metrics -from opentelemetry.sdk.metrics import ValueObserver +from opentelemetry.sdk.metrics import ( + SumObserver, + UpDownSumObserver, + ValueObserver, +) from opentelemetry.sdk.metrics.export import MetricsExporter from opentelemetry.sdk.metrics.export.controller import PushController +from opentelemetry.sdk.util import get_dict_as_key class SystemMetrics: + # pylint: disable=too-many-statements def __init__( self, exporter: MetricsExporter, @@ -79,157 +101,591 @@ class SystemMetrics: self.controller = PushController( meter=self.meter, exporter=exporter, interval=interval ) + self._python_implementation = python_implementation().lower() if config is None: self._config = { - "system_memory": ["total", "available", "used", "free"], - "system_cpu": ["user", "system", "idle"], - "network_bytes": ["bytes_recv", "bytes_sent"], - "runtime_memory": ["rss", "vms"], - "runtime_cpu": ["user", "system"], + "system.cpu.time": ["idle", "user", "system", "irq"], + "system.cpu.utilization": ["idle", "user", "system", "irq"], + "system.memory.usage": ["used", "free", "cached"], + "system.memory.utilization": ["used", "free", "cached"], + "system.swap.usage": ["used", "free"], + "system.swap.utilization": ["used", "free"], + # system.swap.page.faults: [], + # system.swap.page.operations: [], + "system.disk.io": ["read", "write"], + "system.disk.operations": ["read", "write"], + "system.disk.time": ["read", "write"], + "system.disk.merged": ["read", "write"], + # "system.filesystem.usage": [], + # "system.filesystem.utilization": [], + "system.network.dropped.packets": ["transmit", "receive"], + "system.network.packets": ["transmit", "receive"], + "system.network.errors": ["transmit", "receive"], + "system.network.io": ["trasmit", "receive"], + "system.network.connections": ["family", "type"], + "runtime.memory": ["rss", "vms"], + "runtime.cpu.time": ["user", "system"], } else: self._config = config + self._proc = psutil.Process(os.getpid()) - self._system_memory_labels = {} - self._system_cpu_labels = {} - self._network_bytes_labels = {} - self._runtime_memory_labels = {} - self._runtime_cpu_labels = {} - self._runtime_gc_labels = {} - # create the label set for each observer once - for key, value in self._labels.items(): - self._system_memory_labels[key] = value - self._system_cpu_labels[key] = value - self._network_bytes_labels[key] = value - self._runtime_memory_labels[key] = value - self._runtime_gc_labels[key] = value + + self._system_cpu_time_labels = self._labels.copy() + self._system_cpu_utilization_labels = self._labels.copy() + + self._system_memory_usage_labels = self._labels.copy() + self._system_memory_utilization_labels = self._labels.copy() + + self._system_swap_usage_labels = self._labels.copy() + self._system_swap_utilization_labels = self._labels.copy() + # self._system_swap_page_faults = self._labels.copy() + # self._system_swap_page_operations = self._labels.copy() + + self._system_disk_io_labels = self._labels.copy() + self._system_disk_operations_labels = self._labels.copy() + self._system_disk_time_labels = self._labels.copy() + self._system_disk_merged_labels = self._labels.copy() + + # self._system_filesystem_usage_labels = self._labels.copy() + # self._system_filesystem_utilization_labels = self._labels.copy() + + self._system_network_dropped_packets_labels = self._labels.copy() + self._system_network_packets_labels = self._labels.copy() + self._system_network_errors_labels = self._labels.copy() + self._system_network_io_labels = self._labels.copy() + self._system_network_connections_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() self.meter.register_observer( - callback=self._get_system_memory, - name="system.mem", - description="System memory", - unit="bytes", - value_type=int, - observer_type=ValueObserver, + callback=self._get_system_cpu_time, + name="system.cpu.time", + description="System CPU time", + unit="seconds", + value_type=float, + observer_type=SumObserver, ) self.meter.register_observer( - callback=self._get_system_cpu, - name="system.cpu", - description="System CPU", - unit="seconds", + callback=self._get_system_cpu_utilization, + name="system.cpu.utilization", + description="System CPU utilization", + unit="1", value_type=float, observer_type=ValueObserver, ) self.meter.register_observer( - callback=self._get_network_bytes, - name="system.net.bytes", - description="System network bytes", + callback=self._get_system_memory_usage, + name="system.memory.usage", + description="System memory usage", unit="bytes", value_type=int, observer_type=ValueObserver, ) + self.meter.register_observer( + callback=self._get_system_memory_utilization, + name="system.memory.utilization", + description="System memory utilization", + unit="1", + value_type=float, + observer_type=ValueObserver, + ) + + self.meter.register_observer( + callback=self._get_system_swap_usage, + name="system.swap.usage", + description="System swap usage", + unit="pages", + value_type=int, + observer_type=ValueObserver, + ) + + self.meter.register_observer( + callback=self._get_system_swap_utilization, + name="system.swap.utilization", + description="System swap utilization", + unit="1", + value_type=float, + observer_type=ValueObserver, + ) + + # self.meter.register_observer( + # callback=self._get_system_swap_page_faults, + # name="system.swap.page_faults", + # description="System swap page faults", + # unit="faults", + # value_type=int, + # observer_type=SumObserver, + # ) + + # self.meter.register_observer( + # callback=self._get_system_swap_page_operations, + # name="system.swap.page_operations", + # description="System swap page operations", + # unit="operations", + # value_type=int, + # observer_type=SumObserver, + # ) + + self.meter.register_observer( + callback=self._get_system_disk_io, + name="system.disk.io", + description="System disk IO", + unit="bytes", + value_type=int, + observer_type=SumObserver, + ) + + self.meter.register_observer( + callback=self._get_system_disk_operations, + name="system.disk.operations", + description="System disk operations", + unit="operations", + value_type=int, + observer_type=SumObserver, + ) + + self.meter.register_observer( + callback=self._get_system_disk_time, + name="system.disk.time", + description="System disk time", + unit="seconds", + value_type=float, + observer_type=SumObserver, + ) + + self.meter.register_observer( + callback=self._get_system_disk_merged, + name="system.disk.merged", + description="System disk merged", + unit="1", + value_type=int, + observer_type=SumObserver, + ) + + # self.meter.register_observer( + # callback=self._get_system_filesystem_usage, + # name="system.filesystem.usage", + # description="System filesystem usage", + # unit="bytes", + # value_type=int, + # observer_type=ValueObserver, + # ) + + # self.meter.register_observer( + # callback=self._get_system_filesystem_utilization, + # name="system.filesystem.utilization", + # description="System filesystem utilization", + # unit="1", + # value_type=float, + # observer_type=ValueObserver, + # ) + + self.meter.register_observer( + callback=self._get_system_network_dropped_packets, + name="system.network.dropped_packets", + description="System network dropped_packets", + unit="packets", + value_type=int, + observer_type=SumObserver, + ) + + self.meter.register_observer( + callback=self._get_system_network_packets, + name="system.network.packets", + description="System network packets", + unit="packets", + value_type=int, + observer_type=SumObserver, + ) + + self.meter.register_observer( + callback=self._get_system_network_errors, + name="system.network.errors", + description="System network errors", + unit="errors", + value_type=int, + observer_type=SumObserver, + ) + + self.meter.register_observer( + callback=self._get_system_network_io, + name="system.network.io", + description="System network io", + unit="bytes", + value_type=int, + observer_type=SumObserver, + ) + + self.meter.register_observer( + callback=self._get_system_network_connections, + name="system.network.connections", + description="System network connections", + unit="connections", + value_type=int, + observer_type=UpDownSumObserver, + ) + self.meter.register_observer( callback=self._get_runtime_memory, - name="runtime.python.mem", - description="Runtime memory", + name="runtime.{}.memory".format(self._python_implementation), + description="Runtime {} memory".format( + self._python_implementation + ), unit="bytes", value_type=int, - observer_type=ValueObserver, + observer_type=SumObserver, ) self.meter.register_observer( - callback=self._get_runtime_cpu, - name="runtime.python.cpu", - description="Runtime CPU", + callback=self._get_runtime_cpu_time, + name="runtime.{}.cpu_time".format(self._python_implementation), + description="Runtime {} CPU time".format( + self._python_implementation + ), unit="seconds", value_type=float, - observer_type=ValueObserver, + observer_type=SumObserver, ) self.meter.register_observer( callback=self._get_runtime_gc_count, - name="runtime.python.gc.count", - description="Runtime: gc objects", - unit="objects", + name="runtime.{}.gc_count".format(self._python_implementation), + description="Runtime {} GC count".format( + self._python_implementation + ), + unit="bytes", value_type=int, - observer_type=ValueObserver, + observer_type=SumObserver, ) - def _get_system_memory(self, observer: metrics.ValueObserver) -> None: - """Observer callback for memory available + def _get_system_cpu_time(self, observer: metrics.ValueObserver) -> None: + """Observer callback for system CPU time + + Args: + observer: the observer to update + """ + for cpu, times in enumerate(psutil.cpu_times(percpu=True)): + for metric in self._config["system.cpu.time"]: + self._system_cpu_time_labels["state"] = metric + self._system_cpu_time_labels["cpu"] = cpu + 1 + observer.observe( + getattr(times, metric), self._system_cpu_time_labels + ) + + def _get_system_cpu_utilization( + self, observer: metrics.ValueObserver + ) -> None: + """Observer callback for system CPU utilization + + Args: + observer: the observer to update + """ + + for cpu, times_percent in enumerate( + psutil.cpu_times_percent(percpu=True) + ): + for metric in self._config["system.cpu.utilization"]: + self._system_cpu_utilization_labels["state"] = metric + self._system_cpu_utilization_labels["cpu"] = cpu + 1 + observer.observe( + getattr(times_percent, metric) / 100, + self._system_cpu_utilization_labels, + ) + + def _get_system_memory_usage( + self, observer: metrics.ValueObserver + ) -> None: + """Observer callback for memory usage + + Args: + observer: the observer to update + """ + virtual_memory = psutil.virtual_memory() + for metric in self._config["system.memory.usage"]: + self._system_memory_usage_labels["state"] = metric + observer.observe( + getattr(virtual_memory, metric), + self._system_memory_usage_labels, + ) + + def _get_system_memory_utilization( + self, observer: metrics.ValueObserver + ) -> None: + """Observer callback for memory utilization Args: observer: the observer to update """ system_memory = psutil.virtual_memory() - for metric in self._config["system_memory"]: - self._system_memory_labels["type"] = metric + + for metric in self._config["system.memory.utilization"]: + self._system_memory_utilization_labels["state"] = metric observer.observe( - getattr(system_memory, metric), self._system_memory_labels + getattr(system_memory, metric) / system_memory.total, + self._system_memory_utilization_labels, ) - def _get_system_cpu(self, observer: metrics.ValueObserver) -> None: - """Observer callback for system cpu + def _get_system_swap_usage(self, observer: metrics.ValueObserver) -> None: + """Observer callback for swap usage Args: observer: the observer to update """ - cpu_times = psutil.cpu_times() - for _type in self._config["system_cpu"]: - self._system_cpu_labels["type"] = _type + system_swap = psutil.swap_memory() + + for metric in self._config["system.swap.usage"]: + self._system_swap_usage_labels["state"] = metric observer.observe( - getattr(cpu_times, _type), self._system_cpu_labels + getattr(system_swap, metric), self._system_swap_usage_labels ) - def _get_network_bytes(self, observer: metrics.ValueObserver) -> None: - """Observer callback for network bytes + def _get_system_swap_utilization( + self, observer: metrics.ValueObserver + ) -> None: + """Observer callback for swap utilization Args: observer: the observer to update """ - net_io = psutil.net_io_counters() - for _type in self._config["network_bytes"]: - self._network_bytes_labels["type"] = _type + system_swap = psutil.swap_memory() + + for metric in self._config["system.swap.utilization"]: + self._system_swap_utilization_labels["state"] = metric observer.observe( - getattr(net_io, _type), self._network_bytes_labels + getattr(system_swap, metric) / system_swap.total, + self._system_swap_utilization_labels, ) - def _get_runtime_memory(self, observer: metrics.ValueObserver) -> None: + # TODO Add _get_system_swap_page_faults + # TODO Add _get_system_swap_page_operations + + def _get_system_disk_io(self, observer: metrics.SumObserver) -> None: + """Observer callback for disk IO + + Args: + observer: the observer to update + """ + for device, counters in psutil.disk_io_counters(perdisk=True).items(): + for metric in self._config["system.disk.io"]: + self._system_disk_io_labels["device"] = device + self._system_disk_io_labels["direction"] = metric + observer.observe( + getattr(counters, "{}_bytes".format(metric)), + self._system_disk_io_labels, + ) + + def _get_system_disk_operations( + self, observer: metrics.SumObserver + ) -> None: + """Observer callback for disk operations + + Args: + observer: the observer to update + """ + for device, counters in psutil.disk_io_counters(perdisk=True).items(): + for metric in self._config["system.disk.operations"]: + self._system_disk_operations_labels["device"] = device + self._system_disk_operations_labels["direction"] = metric + observer.observe( + getattr(counters, "{}_count".format(metric)), + self._system_disk_operations_labels, + ) + + def _get_system_disk_time(self, observer: metrics.SumObserver) -> None: + """Observer callback for disk time + + Args: + observer: the observer to update + """ + for device, counters in psutil.disk_io_counters(perdisk=True).items(): + for metric in self._config["system.disk.time"]: + self._system_disk_time_labels["device"] = device + self._system_disk_time_labels["direction"] = metric + observer.observe( + getattr(counters, "{}_time".format(metric)) / 1000, + self._system_disk_time_labels, + ) + + def _get_system_disk_merged(self, observer: metrics.SumObserver) -> None: + """Observer callback for disk merged operations + + Args: + observer: the observer to update + """ + + # FIXME The units in the spec is 1, it seems like it should be + # operations or the value type should be Double + + for device, counters in psutil.disk_io_counters(perdisk=True).items(): + for metric in self._config["system.disk.time"]: + self._system_disk_merged_labels["device"] = device + self._system_disk_merged_labels["direction"] = metric + observer.observe( + getattr(counters, "{}_merged_count".format(metric)), + self._system_disk_merged_labels, + ) + + # TODO Add _get_system_filesystem_usage + # TODO Add _get_system_filesystem_utilization + # TODO Filesystem information can be obtained with os.statvfs in Unix-like + # OSs, how to do the same in Windows? + + def _get_system_network_dropped_packets( + self, observer: metrics.SumObserver + ) -> None: + """Observer callback for network dropped packets + + Args: + observer: the observer to update + """ + + for device, counters in psutil.net_io_counters(pernic=True).items(): + for metric in self._config["system.network.dropped.packets"]: + in_out = {"receive": "in", "transmit": "out"}[metric] + self._system_network_dropped_packets_labels["device"] = device + self._system_network_dropped_packets_labels[ + "direction" + ] = metric + observer.observe( + getattr(counters, "drop{}".format(in_out)), + self._system_network_dropped_packets_labels, + ) + + def _get_system_network_packets( + self, observer: metrics.SumObserver + ) -> None: + """Observer callback for network packets + + Args: + observer: the observer to update + """ + + for device, counters in psutil.net_io_counters(pernic=True).items(): + for metric in self._config["system.network.dropped.packets"]: + recv_sent = {"receive": "recv", "transmit": "sent"}[metric] + self._system_network_packets_labels["device"] = device + self._system_network_packets_labels["direction"] = metric + observer.observe( + getattr(counters, "packets_{}".format(recv_sent)), + self._system_network_packets_labels, + ) + + def _get_system_network_errors( + self, observer: metrics.SumObserver + ) -> None: + """Observer callback for network errors + + Args: + observer: the observer to update + """ + for device, counters in psutil.net_io_counters(pernic=True).items(): + for metric in self._config["system.network.errors"]: + in_out = {"receive": "in", "transmit": "out"}[metric] + self._system_network_errors_labels["device"] = device + self._system_network_errors_labels["direction"] = metric + observer.observe( + getattr(counters, "err{}".format(in_out)), + self._system_network_errors_labels, + ) + + def _get_system_network_io(self, observer: metrics.SumObserver) -> None: + """Observer callback for network IO + + Args: + observer: the observer to update + """ + + for device, counters in psutil.net_io_counters(pernic=True).items(): + for metric in self._config["system.network.dropped.packets"]: + recv_sent = {"receive": "recv", "transmit": "sent"}[metric] + self._system_network_io_labels["device"] = device + self._system_network_io_labels["direction"] = metric + observer.observe( + getattr(counters, "bytes_{}".format(recv_sent)), + self._system_network_io_labels, + ) + + def _get_system_network_connections( + self, observer: metrics.UpDownSumObserver + ) -> None: + """Observer callback for network connections + + Args: + observer: the observer to update + """ + # TODO How to find the device identifier for a particular + # connection? + + connection_counters = {} + + for net_connection in psutil.net_connections(): + for metric in self._config["system.network.connections"]: + self._system_network_connections_labels["protocol"] = { + 1: "tcp", + 2: "udp", + }[net_connection.type.value] + self._system_network_connections_labels[ + "state" + ] = net_connection.status + self._system_network_connections_labels[metric] = getattr( + net_connection, metric + ) + + connection_counters_key = get_dict_as_key( + self._system_network_connections_labels + ) + + if connection_counters_key in connection_counters.keys(): + connection_counters[connection_counters_key]["counter"] += 1 + else: + connection_counters[connection_counters_key] = { + "counter": 1, + "labels": self._system_network_connections_labels.copy(), + } + + for connection_counter in connection_counters.values(): + observer.observe( + connection_counter["counter"], connection_counter["labels"], + ) + + def _get_runtime_memory(self, observer: metrics.SumObserver) -> None: """Observer callback for runtime memory Args: observer: the observer to update """ proc_memory = self._proc.memory_info() - for _type in self._config["runtime_memory"]: - self._runtime_memory_labels["type"] = _type + for metric in self._config["runtime.memory"]: + self._runtime_memory_labels["type"] = metric observer.observe( - getattr(proc_memory, _type), self._runtime_memory_labels + getattr(proc_memory, metric), self._runtime_memory_labels, ) - def _get_runtime_cpu(self, observer: metrics.ValueObserver) -> None: - """Observer callback for runtime CPU + def _get_runtime_cpu_time(self, observer: metrics.SumObserver) -> None: + """Observer callback for runtime CPU time Args: observer: the observer to update """ proc_cpu = self._proc.cpu_times() - for _type in self._config["runtime_cpu"]: - self._runtime_cpu_labels["type"] = _type + for metric in self._config["runtime.cpu.time"]: + self._runtime_cpu_time_labels["type"] = metric observer.observe( - getattr(proc_cpu, _type), self._runtime_cpu_labels + getattr(proc_cpu, metric), self._runtime_cpu_time_labels, ) - def _get_runtime_gc_count(self, observer: metrics.ValueObserver) -> None: + def _get_runtime_gc_count(self, observer: metrics.SumObserver) -> None: """Observer callback for garbage collection Args: observer: the observer to update """ - gc_count = gc.get_count() - for index, count in enumerate(gc_count): - self._runtime_gc_labels["count"] = str(index) - observer.observe(count, self._runtime_gc_labels) + for index, count in enumerate(gc.get_count()): + self._runtime_gc_count_labels["count"] = str(index) + observer.observe(count, self._runtime_gc_count_labels) 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 b9ae662af..2f155383f 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py @@ -12,11 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +# pylint: disable=protected-access + from collections import namedtuple +from platform import python_implementation from unittest import mock from opentelemetry import metrics from opentelemetry.instrumentation.system_metrics import SystemMetrics +from opentelemetry.sdk.metrics.export.aggregate import ValueObserverAggregator from opentelemetry.test.test_base import TestBase @@ -24,6 +28,7 @@ class TestSystemMetrics(TestBase): def setUp(self): super().setUp() self.memory_metrics_exporter.clear() + self.implementation = python_implementation().lower() def test_system_metrics_constructor(self): # ensure the observers have been registered @@ -31,15 +36,30 @@ class TestSystemMetrics(TestBase): with mock.patch("opentelemetry.metrics.get_meter") as mock_get_meter: mock_get_meter.return_value = meter SystemMetrics(self.memory_metrics_exporter) - self.assertEqual(len(meter.observers), 6) + + self.assertEqual(len(meter.observers), 18) + observer_names = [ - "system.mem", - "system.cpu", - "system.net.bytes", - "runtime.python.mem", - "runtime.python.cpu", - "runtime.python.gc.count", + "system.cpu.time", + "system.cpu.utilization", + "system.memory.usage", + "system.memory.utilization", + "system.swap.usage", + "system.swap.utilization", + "system.disk.io", + "system.disk.operations", + "system.disk.time", + "system.disk.merged", + "system.network.dropped_packets", + "system.network.packets", + "system.network.errors", + "system.network.io", + "system.network.connections", + "runtime.{}.memory".format(self.implementation), + "runtime.{}.cpu_time".format(self.implementation), + "runtime.{}.gc_count".format(self.implementation), ] + for observer in meter.observers: self.assertIn(observer.name, observer_names) observer_names.remove(observer.name) @@ -57,7 +77,7 @@ class TestSystemMetrics(TestBase): and metric.instrument.name == observer_name ): self.assertEqual( - metric.aggregator.checkpoint.last, expected[metric.labels], + metric.aggregator.checkpoint, expected[metric.labels], ) assertions += 1 self.assertEqual(len(expected), assertions) @@ -70,134 +90,614 @@ class TestSystemMetrics(TestBase): system_metrics = SystemMetrics(self.memory_metrics_exporter) self._assert_metrics(observer_name, system_metrics, expected) + # When this test case is executed, _get_system_cpu_utilization gets run + # too because of the controller thread which runs all observers. This patch + # is added here to stop a warning that would otherwise be raised. + # pylint: disable=unused-argument + @mock.patch("psutil.cpu_times_percent") @mock.patch("psutil.cpu_times") - def test_system_cpu(self, mock_cpu_times): - CPUTimes = namedtuple("CPUTimes", ["user", "nice", "system", "idle"]) - mock_cpu_times.return_value = CPUTimes( - user=332277.48, nice=0.0, system=309836.43, idle=6724698.94 - ) - - expected = { - (("type", "user"),): 332277.48, - (("type", "system"),): 309836.43, - (("type", "idle"),): 6724698.94, - } - self._test_metrics("system.cpu", expected) - - @mock.patch("psutil.virtual_memory") - def test_system_memory(self, mock_virtual_memory): - VirtualMemory = namedtuple( - "VirtualMemory", - [ - "total", - "available", - "percent", - "used", - "free", - "active", - "inactive", - "wired", - ], - ) - mock_virtual_memory.return_value = VirtualMemory( - total=17179869184, - available=5520928768, - percent=67.9, - used=10263990272, - free=266964992, - active=5282459648, - inactive=5148700672, - wired=4981530624, - ) - - expected = { - (("type", "total"),): 17179869184, - (("type", "used"),): 10263990272, - (("type", "available"),): 5520928768, - (("type", "free"),): 266964992, - } - self._test_metrics("system.mem", expected) - - @mock.patch("psutil.net_io_counters") - def test_network_bytes(self, mock_net_io_counters): - NetworkIO = namedtuple( - "NetworkIO", - ["bytes_sent", "bytes_recv", "packets_recv", "packets_sent"], - ) - mock_net_io_counters.return_value = NetworkIO( - bytes_sent=23920188416, - bytes_recv=46798894080, - packets_sent=53127118, - packets_recv=53205738, - ) - - expected = { - (("type", "bytes_recv"),): 46798894080, - (("type", "bytes_sent"),): 23920188416, - } - self._test_metrics("system.net.bytes", expected) - - def test_runtime_memory(self): - meter = self.meter_provider.get_meter(__name__) - with mock.patch("opentelemetry.metrics.get_meter") as mock_get_meter: - mock_get_meter.return_value = meter - system_metrics = SystemMetrics(self.memory_metrics_exporter) - - with mock.patch.object( - system_metrics._proc, # pylint: disable=protected-access - "memory_info", - ) as mock_runtime_memory: - RuntimeMemory = namedtuple( - "RuntimeMemory", ["rss", "vms", "pfaults", "pageins"], - ) - mock_runtime_memory.return_value = RuntimeMemory( - rss=9777152, vms=4385665024, pfaults=2631, pageins=49 - ) - expected = { - (("type", "rss"),): 9777152, - (("type", "vms"),): 4385665024, - } - self._assert_metrics( - "runtime.python.mem", system_metrics, expected - ) - - def test_runtime_cpu(self): - meter = self.meter_provider.get_meter(__name__) - with mock.patch("opentelemetry.metrics.get_meter") as mock_get_meter: - mock_get_meter.return_value = meter - system_metrics = SystemMetrics(self.memory_metrics_exporter) - - with mock.patch.object( - system_metrics._proc, # pylint: disable=protected-access - "cpu_times", - ) as mock_runtime_cpu_times: - RuntimeCPU = namedtuple( - "RuntimeCPU", ["user", "nice", "system"] - ) - mock_runtime_cpu_times.return_value = RuntimeCPU( - user=100.48, nice=0.0, system=200.43 - ) - - expected = { - (("type", "user"),): 100.48, - (("type", "system"),): 200.43, - } - - self._assert_metrics( - "runtime.python.cpu", system_metrics, expected - ) - - @mock.patch("gc.get_count") - def test_runtime_gc_count(self, mock_gc): - mock_gc.return_value = [ - 100, # gen0 - 50, # gen1 - 10, # gen2 + def test_system_cpu_time(self, mock_cpu_times, mock_cpu_times_percent): + CPUTimes = namedtuple("CPUTimes", ["idle", "user", "system", "irq"]) + mock_cpu_times.return_value = [ + CPUTimes(idle=1.2, user=3.4, system=5.6, irq=7.8), + CPUTimes(idle=1.2, user=3.4, system=5.6, irq=7.8), ] expected = { - (("count", "0"),): 100, - (("count", "1"),): 50, - (("count", "2"),): 10, + (("cpu", 1), ("state", "idle"),): 1.2, + (("cpu", 1), ("state", "user"),): 3.4, + (("cpu", 1), ("state", "system"),): 5.6, + (("cpu", 1), ("state", "irq"),): 7.8, + (("cpu", 2), ("state", "idle"),): 1.2, + (("cpu", 2), ("state", "user"),): 3.4, + (("cpu", 2), ("state", "system"),): 5.6, + (("cpu", 2), ("state", "irq"),): 7.8, } - self._test_metrics("runtime.python.gc.count", expected) + self._test_metrics("system.cpu.time", expected) + + @mock.patch("psutil.cpu_times_percent") + def test_system_cpu_utilization(self, mock_cpu_times_percent): + CPUTimesPercent = namedtuple( + "CPUTimesPercent", ["idle", "user", "system", "irq"] + ) + mock_cpu_times_percent.return_value = [ + CPUTimesPercent(idle=1.2, user=3.4, system=5.6, irq=7.8), + CPUTimesPercent(idle=1.2, user=3.4, system=5.6, irq=7.8), + ] + + expected = { + (("cpu", 1), ("state", "idle"),): ValueObserverAggregator._TYPE( + min=1.2 / 100, + max=1.2 / 100, + sum=1.2 / 100, + count=1, + last=1.2 / 100, + ), + (("cpu", 1), ("state", "user"),): ValueObserverAggregator._TYPE( + min=3.4 / 100, + max=3.4 / 100, + sum=3.4 / 100, + count=1, + last=3.4 / 100, + ), + (("cpu", 1), ("state", "system"),): ValueObserverAggregator._TYPE( + min=5.6 / 100, + max=5.6 / 100, + sum=5.6 / 100, + count=1, + last=5.6 / 100, + ), + (("cpu", 1), ("state", "irq"),): ValueObserverAggregator._TYPE( + min=7.8 / 100, + max=7.8 / 100, + sum=7.8 / 100, + count=1, + last=7.8 / 100, + ), + (("cpu", 2), ("state", "idle"),): ValueObserverAggregator._TYPE( + min=1.2 / 100, + max=1.2 / 100, + sum=1.2 / 100, + count=1, + last=1.2 / 100, + ), + (("cpu", 2), ("state", "user"),): ValueObserverAggregator._TYPE( + min=3.4 / 100, + max=3.4 / 100, + sum=3.4 / 100, + count=1, + last=3.4 / 100, + ), + (("cpu", 2), ("state", "system"),): ValueObserverAggregator._TYPE( + min=5.6 / 100, + max=5.6 / 100, + sum=5.6 / 100, + count=1, + last=5.6 / 100, + ), + (("cpu", 2), ("state", "irq"),): ValueObserverAggregator._TYPE( + min=7.8 / 100, + max=7.8 / 100, + sum=7.8 / 100, + count=1, + last=7.8 / 100, + ), + } + self._test_metrics("system.cpu.utilization", expected) + + @mock.patch("psutil.virtual_memory") + def test_system_memory_usage(self, mock_virtual_memory): + VirtualMemory = namedtuple( + "VirtualMemory", ["used", "free", "cached", "total"] + ) + mock_virtual_memory.return_value = VirtualMemory( + used=1, free=2, cached=3, total=4 + ) + + expected = { + (("state", "used"),): ValueObserverAggregator._TYPE( + min=1, max=1, sum=1, count=1, last=1 + ), + (("state", "free"),): ValueObserverAggregator._TYPE( + min=2, max=2, sum=2, count=1, last=2 + ), + (("state", "cached"),): ValueObserverAggregator._TYPE( + min=3, max=3, sum=3, count=1, last=3 + ), + } + self._test_metrics("system.memory.usage", expected) + + @mock.patch("psutil.virtual_memory") + def test_system_memory_utilization(self, mock_virtual_memory): + VirtualMemory = namedtuple( + "VirtualMemory", ["used", "free", "cached", "total"] + ) + mock_virtual_memory.return_value = VirtualMemory( + used=1, free=2, cached=3, total=4 + ) + + expected = { + (("state", "used"),): ValueObserverAggregator._TYPE( + min=1 / 4, max=1 / 4, sum=1 / 4, count=1, last=1 / 4 + ), + (("state", "free"),): ValueObserverAggregator._TYPE( + min=2 / 4, max=2 / 4, sum=2 / 4, count=1, last=2 / 4 + ), + (("state", "cached"),): ValueObserverAggregator._TYPE( + min=3 / 4, max=3 / 4, sum=3 / 4, count=1, last=3 / 4 + ), + } + self._test_metrics("system.memory.utilization", expected) + + @mock.patch("psutil.swap_memory") + def test_system_swap_usage(self, mock_swap_memory): + SwapMemory = namedtuple("SwapMemory", ["used", "free", "total"]) + mock_swap_memory.return_value = SwapMemory(used=1, free=2, total=3) + + expected = { + (("state", "used"),): ValueObserverAggregator._TYPE( + min=1, max=1, sum=1, count=1, last=1 + ), + (("state", "free"),): ValueObserverAggregator._TYPE( + min=2, max=2, sum=2, count=1, last=2 + ), + } + self._test_metrics("system.swap.usage", expected) + + @mock.patch("psutil.swap_memory") + def test_system_swap_utilization(self, mock_swap_memory): + SwapMemory = namedtuple("SwapMemory", ["used", "free", "total"]) + mock_swap_memory.return_value = SwapMemory(used=1, free=2, total=3) + + expected = { + (("state", "used"),): ValueObserverAggregator._TYPE( + min=1 / 3, max=1 / 3, sum=1 / 3, count=1, last=1 / 3 + ), + (("state", "free"),): ValueObserverAggregator._TYPE( + min=2 / 3, max=2 / 3, sum=2 / 3, count=1, last=2 / 3 + ), + } + self._test_metrics("system.swap.utilization", expected) + + @mock.patch("psutil.disk_io_counters") + def test_system_disk_io(self, mock_disk_io_counters): + DiskIO = namedtuple( + "DiskIO", + [ + "read_count", + "write_count", + "read_bytes", + "write_bytes", + "read_time", + "write_time", + "read_merged_count", + "write_merged_count", + ], + ) + mock_disk_io_counters.return_value = { + "sda": DiskIO( + read_count=1, + write_count=2, + read_bytes=3, + write_bytes=4, + read_time=5, + write_time=6, + read_merged_count=7, + write_merged_count=8, + ), + "sdb": DiskIO( + read_count=9, + write_count=10, + read_bytes=11, + write_bytes=12, + read_time=13, + write_time=14, + read_merged_count=15, + write_merged_count=16, + ), + } + + expected = { + (("device", "sda"), ("direction", "read"),): 3, + (("device", "sda"), ("direction", "write"),): 4, + (("device", "sdb"), ("direction", "read"),): 11, + (("device", "sdb"), ("direction", "write"),): 12, + } + self._test_metrics("system.disk.io", expected) + + @mock.patch("psutil.disk_io_counters") + def test_system_disk_operations(self, mock_disk_io_counters): + DiskIO = namedtuple( + "DiskIO", + [ + "read_count", + "write_count", + "read_bytes", + "write_bytes", + "read_time", + "write_time", + "read_merged_count", + "write_merged_count", + ], + ) + mock_disk_io_counters.return_value = { + "sda": DiskIO( + read_count=1, + write_count=2, + read_bytes=3, + write_bytes=4, + read_time=5, + write_time=6, + read_merged_count=7, + write_merged_count=8, + ), + "sdb": DiskIO( + read_count=9, + write_count=10, + read_bytes=11, + write_bytes=12, + read_time=13, + write_time=14, + read_merged_count=15, + write_merged_count=16, + ), + } + + expected = { + (("device", "sda"), ("direction", "read"),): 1, + (("device", "sda"), ("direction", "write"),): 2, + (("device", "sdb"), ("direction", "read"),): 9, + (("device", "sdb"), ("direction", "write"),): 10, + } + self._test_metrics("system.disk.operations", expected) + + @mock.patch("psutil.disk_io_counters") + def test_system_disk_time(self, mock_disk_io_counters): + DiskIO = namedtuple( + "DiskIO", + [ + "read_count", + "write_count", + "read_bytes", + "write_bytes", + "read_time", + "write_time", + "read_merged_count", + "write_merged_count", + ], + ) + mock_disk_io_counters.return_value = { + "sda": DiskIO( + read_count=1, + write_count=2, + read_bytes=3, + write_bytes=4, + read_time=5, + write_time=6, + read_merged_count=7, + write_merged_count=8, + ), + "sdb": DiskIO( + read_count=9, + write_count=10, + read_bytes=11, + write_bytes=12, + read_time=13, + write_time=14, + read_merged_count=15, + write_merged_count=16, + ), + } + + expected = { + (("device", "sda"), ("direction", "read"),): 5 / 1000, + (("device", "sda"), ("direction", "write"),): 6 / 1000, + (("device", "sdb"), ("direction", "read"),): 13 / 1000, + (("device", "sdb"), ("direction", "write"),): 14 / 1000, + } + self._test_metrics("system.disk.time", expected) + + @mock.patch("psutil.disk_io_counters") + def test_system_disk_merged(self, mock_disk_io_counters): + DiskIO = namedtuple( + "DiskIO", + [ + "read_count", + "write_count", + "read_bytes", + "write_bytes", + "read_time", + "write_time", + "read_merged_count", + "write_merged_count", + ], + ) + mock_disk_io_counters.return_value = { + "sda": DiskIO( + read_count=1, + write_count=2, + read_bytes=3, + write_bytes=4, + read_time=5, + write_time=6, + read_merged_count=7, + write_merged_count=8, + ), + "sdb": DiskIO( + read_count=9, + write_count=10, + read_bytes=11, + write_bytes=12, + read_time=13, + write_time=14, + read_merged_count=15, + write_merged_count=16, + ), + } + + expected = { + (("device", "sda"), ("direction", "read"),): 7, + (("device", "sda"), ("direction", "write"),): 8, + (("device", "sdb"), ("direction", "read"),): 15, + (("device", "sdb"), ("direction", "write"),): 16, + } + self._test_metrics("system.disk.merged", expected) + + @mock.patch("psutil.net_io_counters") + def test_system_network_dropped_packets(self, mock_net_io_counters): + NetIO = namedtuple( + "NetIO", + [ + "dropin", + "dropout", + "packets_sent", + "packets_recv", + "errin", + "errout", + "bytes_sent", + "bytes_recv", + ], + ) + mock_net_io_counters.return_value = { + "eth0": NetIO( + dropin=1, + dropout=2, + packets_sent=3, + packets_recv=4, + errin=5, + errout=6, + bytes_sent=7, + bytes_recv=8, + ), + "eth1": NetIO( + dropin=9, + dropout=10, + packets_sent=11, + packets_recv=12, + errin=13, + errout=14, + bytes_sent=15, + bytes_recv=16, + ), + } + + expected = { + (("device", "eth0"), ("direction", "receive"),): 1, + (("device", "eth0"), ("direction", "transmit"),): 2, + (("device", "eth1"), ("direction", "receive"),): 9, + (("device", "eth1"), ("direction", "transmit"),): 10, + } + self._test_metrics("system.network.dropped_packets", expected) + + @mock.patch("psutil.net_io_counters") + def test_system_network_packets(self, mock_net_io_counters): + NetIO = namedtuple( + "NetIO", + [ + "dropin", + "dropout", + "packets_sent", + "packets_recv", + "errin", + "errout", + "bytes_sent", + "bytes_recv", + ], + ) + mock_net_io_counters.return_value = { + "eth0": NetIO( + dropin=1, + dropout=2, + packets_sent=3, + packets_recv=4, + errin=5, + errout=6, + bytes_sent=7, + bytes_recv=8, + ), + "eth1": NetIO( + dropin=9, + dropout=10, + packets_sent=11, + packets_recv=12, + errin=13, + errout=14, + bytes_sent=15, + bytes_recv=16, + ), + } + + expected = { + (("device", "eth0"), ("direction", "receive"),): 4, + (("device", "eth0"), ("direction", "transmit"),): 3, + (("device", "eth1"), ("direction", "receive"),): 12, + (("device", "eth1"), ("direction", "transmit"),): 11, + } + self._test_metrics("system.network.packets", expected) + + @mock.patch("psutil.net_io_counters") + def test_system_network_errors(self, mock_net_io_counters): + NetIO = namedtuple( + "NetIO", + [ + "dropin", + "dropout", + "packets_sent", + "packets_recv", + "errin", + "errout", + "bytes_sent", + "bytes_recv", + ], + ) + mock_net_io_counters.return_value = { + "eth0": NetIO( + dropin=1, + dropout=2, + packets_sent=3, + packets_recv=4, + errin=5, + errout=6, + bytes_sent=7, + bytes_recv=8, + ), + "eth1": NetIO( + dropin=9, + dropout=10, + packets_sent=11, + packets_recv=12, + errin=13, + errout=14, + bytes_sent=15, + bytes_recv=16, + ), + } + + expected = { + (("device", "eth0"), ("direction", "receive"),): 5, + (("device", "eth0"), ("direction", "transmit"),): 6, + (("device", "eth1"), ("direction", "receive"),): 13, + (("device", "eth1"), ("direction", "transmit"),): 14, + } + self._test_metrics("system.network.errors", expected) + + @mock.patch("psutil.net_io_counters") + def test_system_network_io(self, mock_net_io_counters): + NetIO = namedtuple( + "NetIO", + [ + "dropin", + "dropout", + "packets_sent", + "packets_recv", + "errin", + "errout", + "bytes_sent", + "bytes_recv", + ], + ) + mock_net_io_counters.return_value = { + "eth0": NetIO( + dropin=1, + dropout=2, + packets_sent=3, + packets_recv=4, + errin=5, + errout=6, + bytes_sent=7, + bytes_recv=8, + ), + "eth1": NetIO( + dropin=9, + dropout=10, + packets_sent=11, + packets_recv=12, + errin=13, + errout=14, + bytes_sent=15, + bytes_recv=16, + ), + } + + expected = { + (("device", "eth0"), ("direction", "receive"),): 8, + (("device", "eth0"), ("direction", "transmit"),): 7, + (("device", "eth1"), ("direction", "receive"),): 16, + (("device", "eth1"), ("direction", "transmit"),): 15, + } + self._test_metrics("system.network.io", expected) + + @mock.patch("psutil.net_connections") + def test_system_network_connections(self, mock_net_connections): + NetConnection = namedtuple( + "NetworkConnection", ["family", "type", "status"] + ) + Type = namedtuple("Type", ["value"]) + mock_net_connections.return_value = [ + NetConnection(family=1, status="ESTABLISHED", type=Type(value=2),), + NetConnection(family=1, status="ESTABLISHED", type=Type(value=1),), + ] + + expected = { + ( + ("family", 1), + ("protocol", "udp"), + ("state", "ESTABLISHED"), + ("type", Type(value=2)), + ): 1, + ( + ("family", 1), + ("protocol", "tcp"), + ("state", "ESTABLISHED"), + ("type", Type(value=1)), + ): 1, + } + self._test_metrics("system.network.connections", expected) + + @mock.patch("psutil.Process.memory_info") + def test_runtime_memory(self, mock_process_memory_info): + + PMem = namedtuple("PMem", ["rss", "vms"]) + + mock_process_memory_info.configure_mock( + **{"return_value": PMem(rss=1, vms=2)} + ) + + expected = { + (("type", "rss"),): 1, + (("type", "vms"),): 2, + } + self._test_metrics( + "runtime.{}.memory".format(self.implementation), expected + ) + + @mock.patch("psutil.Process.cpu_times") + def test_runtime_cpu_time(self, mock_process_cpu_times): + + PCPUTimes = namedtuple("PCPUTimes", ["user", "system"]) + + mock_process_cpu_times.configure_mock( + **{"return_value": PCPUTimes(user=1.1, system=2.2)} + ) + + expected = { + (("type", "user"),): 1.1, + (("type", "system"),): 2.2, + } + self._test_metrics( + "runtime.{}.cpu_time".format(self.implementation), expected + ) + + @mock.patch("gc.get_count") + def test_runtime_get_count(self, mock_gc_get_count): + + mock_gc_get_count.configure_mock(**{"return_value": (1, 2, 3)}) + + expected = { + (("count", "0"),): 1, + (("count", "1"),): 2, + (("count", "2"),): 3, + } + self._test_metrics( + "runtime.{}.gc_count".format(self.implementation), expected + ) From 6bc4ec293d5735f1427658b6383d941c1656e6fc Mon Sep 17 00:00:00 2001 From: alrex Date: Mon, 14 Sep 2020 15:11:56 -0700 Subject: [PATCH 5/9] dropping support for python 3.4 (#1099) * dropping support for python 3.4 --- .../opentelemetry-instrumentation-system-metrics/CHANGELOG.md | 3 +++ .../opentelemetry-instrumentation-system-metrics/setup.cfg | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md index 4bd1389cc..ac192b60c 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Drop support for Python 3.4 + ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) + ## Version 0.12b0 Released 2020-08-14 diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg index 70c784c60..b7fbb90cb 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg @@ -28,14 +28,13 @@ classifiers = License :: OSI Approved :: Apache Software License Programming Language :: Python Programming Language :: Python :: 3 - Programming Language :: Python :: 3.4 Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 [options] -python_requires = >=3.4 +python_requires = >=3.5 package_dir= =src packages=find_namespace: From 80aa66d3866f778596c04f65fe414ca5b9f8aa43 Mon Sep 17 00:00:00 2001 From: alrex Date: Thu, 17 Sep 2020 08:23:52 -0700 Subject: [PATCH 6/9] release: updating changelogs and version to 0.13b0 (#1129) * updating changelogs and version to 0.13b0 --- .../CHANGELOG.md | 4 ++++ .../opentelemetry-instrumentation-system-metrics/setup.cfg | 6 +++--- .../opentelemetry/instrumentation/system_metrics/version.py | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md index ac192b60c..7a655ad23 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## Version 0.13b0 + +Released 2020-09-17 + - Drop support for Python 3.4 ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg index b7fbb90cb..2aa55ad66 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg @@ -39,13 +39,13 @@ package_dir= =src packages=find_namespace: install_requires = - opentelemetry-api == 0.13dev0 - opentelemetry-sdk == 0.13dev0 + opentelemetry-api == 0.13b0 + opentelemetry-sdk == 0.13b0 psutil ~= 5.7.0 [options.extras_require] test = - opentelemetry-test == 0.13dev0 + opentelemetry-test == 0.13b0 [options.packages.find] where = src diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py index 9cc445d09..2015e87c7 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.13dev0" +__version__ = "0.13b0" From 629597e883110e35bc000ad9f3fc9b2e4f36ac4f Mon Sep 17 00:00:00 2001 From: alrex Date: Thu, 17 Sep 2020 12:21:39 -0700 Subject: [PATCH 7/9] chore: bump dev version (#1131) --- .../opentelemetry-instrumentation-system-metrics/setup.cfg | 6 +++--- .../opentelemetry/instrumentation/system_metrics/version.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg index 2aa55ad66..345393ac8 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg @@ -39,13 +39,13 @@ package_dir= =src packages=find_namespace: install_requires = - opentelemetry-api == 0.13b0 - opentelemetry-sdk == 0.13b0 + opentelemetry-api == 0.14.dev0 + opentelemetry-sdk == 0.14.dev0 psutil ~= 5.7.0 [options.extras_require] test = - opentelemetry-test == 0.13b0 + opentelemetry-test == 0.14.dev0 [options.packages.find] where = src diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py index 2015e87c7..0f9902789 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.13b0" +__version__ = "0.14.dev0" From a1bb8772aef3fd1c00eb79cee56161360e6bd6f6 Mon Sep 17 00:00:00 2001 From: alrex Date: Tue, 6 Oct 2020 08:23:27 -0700 Subject: [PATCH 8/9] Fix issue when metrics are not available (#1207) Not all configured metrics are available on all operating systems, added a hasattr call before calling the observer. --- .../CHANGELOG.md | 3 + .../system_metrics/__init__.py | 189 ++++++++++-------- 2 files changed, 107 insertions(+), 85 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md index 7a655ad23..7405cd91f 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Fix issue when specific metrics are not available in certain OS + ([#1207](https://github.com/open-telemetry/opentelemetry-python/pull/1207)) + ## Version 0.13b0 Released 2020-09-17 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 9ca36d00b..71935aa8d 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 @@ -372,11 +372,12 @@ class SystemMetrics: """ for cpu, times in enumerate(psutil.cpu_times(percpu=True)): for metric in self._config["system.cpu.time"]: - self._system_cpu_time_labels["state"] = metric - self._system_cpu_time_labels["cpu"] = cpu + 1 - observer.observe( - getattr(times, metric), self._system_cpu_time_labels - ) + if hasattr(times, metric): + self._system_cpu_time_labels["state"] = metric + self._system_cpu_time_labels["cpu"] = cpu + 1 + observer.observe( + getattr(times, metric), self._system_cpu_time_labels + ) def _get_system_cpu_utilization( self, observer: metrics.ValueObserver @@ -391,12 +392,13 @@ class SystemMetrics: psutil.cpu_times_percent(percpu=True) ): for metric in self._config["system.cpu.utilization"]: - self._system_cpu_utilization_labels["state"] = metric - self._system_cpu_utilization_labels["cpu"] = cpu + 1 - observer.observe( - getattr(times_percent, metric) / 100, - self._system_cpu_utilization_labels, - ) + if hasattr(times_percent, metric): + self._system_cpu_utilization_labels["state"] = metric + self._system_cpu_utilization_labels["cpu"] = cpu + 1 + observer.observe( + getattr(times_percent, metric) / 100, + self._system_cpu_utilization_labels, + ) def _get_system_memory_usage( self, observer: metrics.ValueObserver @@ -409,10 +411,11 @@ class SystemMetrics: virtual_memory = psutil.virtual_memory() for metric in self._config["system.memory.usage"]: self._system_memory_usage_labels["state"] = metric - observer.observe( - getattr(virtual_memory, metric), - self._system_memory_usage_labels, - ) + if hasattr(virtual_memory, metric): + observer.observe( + getattr(virtual_memory, metric), + self._system_memory_usage_labels, + ) def _get_system_memory_utilization( self, observer: metrics.ValueObserver @@ -426,10 +429,11 @@ class SystemMetrics: for metric in self._config["system.memory.utilization"]: self._system_memory_utilization_labels["state"] = metric - observer.observe( - getattr(system_memory, metric) / system_memory.total, - self._system_memory_utilization_labels, - ) + if hasattr(system_memory, metric): + observer.observe( + getattr(system_memory, metric) / system_memory.total, + self._system_memory_utilization_labels, + ) def _get_system_swap_usage(self, observer: metrics.ValueObserver) -> None: """Observer callback for swap usage @@ -441,9 +445,11 @@ class SystemMetrics: for metric in self._config["system.swap.usage"]: self._system_swap_usage_labels["state"] = metric - observer.observe( - getattr(system_swap, metric), self._system_swap_usage_labels - ) + if hasattr(system_swap, metric): + observer.observe( + getattr(system_swap, metric), + self._system_swap_usage_labels, + ) def _get_system_swap_utilization( self, observer: metrics.ValueObserver @@ -456,11 +462,12 @@ class SystemMetrics: system_swap = psutil.swap_memory() for metric in self._config["system.swap.utilization"]: - self._system_swap_utilization_labels["state"] = metric - observer.observe( - getattr(system_swap, metric) / system_swap.total, - self._system_swap_utilization_labels, - ) + if hasattr(system_swap, metric): + self._system_swap_utilization_labels["state"] = metric + observer.observe( + getattr(system_swap, metric) / system_swap.total, + self._system_swap_utilization_labels, + ) # TODO Add _get_system_swap_page_faults # TODO Add _get_system_swap_page_operations @@ -473,12 +480,13 @@ class SystemMetrics: """ for device, counters in psutil.disk_io_counters(perdisk=True).items(): for metric in self._config["system.disk.io"]: - self._system_disk_io_labels["device"] = device - self._system_disk_io_labels["direction"] = metric - observer.observe( - getattr(counters, "{}_bytes".format(metric)), - self._system_disk_io_labels, - ) + if hasattr(counters, "{}_bytes".format(metric)): + self._system_disk_io_labels["device"] = device + self._system_disk_io_labels["direction"] = metric + observer.observe( + getattr(counters, "{}_bytes".format(metric)), + self._system_disk_io_labels, + ) def _get_system_disk_operations( self, observer: metrics.SumObserver @@ -490,12 +498,13 @@ class SystemMetrics: """ for device, counters in psutil.disk_io_counters(perdisk=True).items(): for metric in self._config["system.disk.operations"]: - self._system_disk_operations_labels["device"] = device - self._system_disk_operations_labels["direction"] = metric - observer.observe( - getattr(counters, "{}_count".format(metric)), - self._system_disk_operations_labels, - ) + if hasattr(counters, "{}_count".format(metric)): + self._system_disk_operations_labels["device"] = device + self._system_disk_operations_labels["direction"] = metric + observer.observe( + getattr(counters, "{}_count".format(metric)), + self._system_disk_operations_labels, + ) def _get_system_disk_time(self, observer: metrics.SumObserver) -> None: """Observer callback for disk time @@ -505,12 +514,13 @@ class SystemMetrics: """ for device, counters in psutil.disk_io_counters(perdisk=True).items(): for metric in self._config["system.disk.time"]: - self._system_disk_time_labels["device"] = device - self._system_disk_time_labels["direction"] = metric - observer.observe( - getattr(counters, "{}_time".format(metric)) / 1000, - self._system_disk_time_labels, - ) + if hasattr(counters, "{}_time".format(metric)): + self._system_disk_time_labels["device"] = device + self._system_disk_time_labels["direction"] = metric + observer.observe( + getattr(counters, "{}_time".format(metric)) / 1000, + self._system_disk_time_labels, + ) def _get_system_disk_merged(self, observer: metrics.SumObserver) -> None: """Observer callback for disk merged operations @@ -524,12 +534,13 @@ class SystemMetrics: for device, counters in psutil.disk_io_counters(perdisk=True).items(): for metric in self._config["system.disk.time"]: - self._system_disk_merged_labels["device"] = device - self._system_disk_merged_labels["direction"] = metric - observer.observe( - getattr(counters, "{}_merged_count".format(metric)), - self._system_disk_merged_labels, - ) + if hasattr(counters, "{}_merged_count".format(metric)): + self._system_disk_merged_labels["device"] = device + self._system_disk_merged_labels["direction"] = metric + observer.observe( + getattr(counters, "{}_merged_count".format(metric)), + self._system_disk_merged_labels, + ) # TODO Add _get_system_filesystem_usage # TODO Add _get_system_filesystem_utilization @@ -548,14 +559,17 @@ class SystemMetrics: for device, counters in psutil.net_io_counters(pernic=True).items(): for metric in self._config["system.network.dropped.packets"]: in_out = {"receive": "in", "transmit": "out"}[metric] - self._system_network_dropped_packets_labels["device"] = device - self._system_network_dropped_packets_labels[ - "direction" - ] = metric - observer.observe( - getattr(counters, "drop{}".format(in_out)), - self._system_network_dropped_packets_labels, - ) + if hasattr(counters, "drop{}".format(in_out)): + self._system_network_dropped_packets_labels[ + "device" + ] = device + self._system_network_dropped_packets_labels[ + "direction" + ] = metric + observer.observe( + getattr(counters, "drop{}".format(in_out)), + self._system_network_dropped_packets_labels, + ) def _get_system_network_packets( self, observer: metrics.SumObserver @@ -569,12 +583,13 @@ class SystemMetrics: for device, counters in psutil.net_io_counters(pernic=True).items(): for metric in self._config["system.network.dropped.packets"]: recv_sent = {"receive": "recv", "transmit": "sent"}[metric] - self._system_network_packets_labels["device"] = device - self._system_network_packets_labels["direction"] = metric - observer.observe( - getattr(counters, "packets_{}".format(recv_sent)), - self._system_network_packets_labels, - ) + if hasattr(counters, "packets_{}".format(recv_sent)): + self._system_network_packets_labels["device"] = device + self._system_network_packets_labels["direction"] = metric + observer.observe( + getattr(counters, "packets_{}".format(recv_sent)), + self._system_network_packets_labels, + ) def _get_system_network_errors( self, observer: metrics.SumObserver @@ -587,12 +602,13 @@ class SystemMetrics: for device, counters in psutil.net_io_counters(pernic=True).items(): for metric in self._config["system.network.errors"]: in_out = {"receive": "in", "transmit": "out"}[metric] - self._system_network_errors_labels["device"] = device - self._system_network_errors_labels["direction"] = metric - observer.observe( - getattr(counters, "err{}".format(in_out)), - self._system_network_errors_labels, - ) + if hasattr(counters, "err{}".format(in_out)): + self._system_network_errors_labels["device"] = device + self._system_network_errors_labels["direction"] = metric + observer.observe( + getattr(counters, "err{}".format(in_out)), + self._system_network_errors_labels, + ) def _get_system_network_io(self, observer: metrics.SumObserver) -> None: """Observer callback for network IO @@ -604,12 +620,13 @@ class SystemMetrics: for device, counters in psutil.net_io_counters(pernic=True).items(): for metric in self._config["system.network.dropped.packets"]: recv_sent = {"receive": "recv", "transmit": "sent"}[metric] - self._system_network_io_labels["device"] = device - self._system_network_io_labels["direction"] = metric - observer.observe( - getattr(counters, "bytes_{}".format(recv_sent)), - self._system_network_io_labels, - ) + if hasattr(counters, "bytes_{}".format(recv_sent)): + self._system_network_io_labels["device"] = device + self._system_network_io_labels["direction"] = metric + observer.observe( + getattr(counters, "bytes_{}".format(recv_sent)), + self._system_network_io_labels, + ) def _get_system_network_connections( self, observer: metrics.UpDownSumObserver @@ -662,10 +679,11 @@ class SystemMetrics: """ proc_memory = self._proc.memory_info() for metric in self._config["runtime.memory"]: - self._runtime_memory_labels["type"] = metric - observer.observe( - getattr(proc_memory, metric), self._runtime_memory_labels, - ) + if hasattr(proc_memory, metric): + self._runtime_memory_labels["type"] = metric + observer.observe( + getattr(proc_memory, metric), self._runtime_memory_labels, + ) def _get_runtime_cpu_time(self, observer: metrics.SumObserver) -> None: """Observer callback for runtime CPU time @@ -675,10 +693,11 @@ class SystemMetrics: """ proc_cpu = self._proc.cpu_times() for metric in self._config["runtime.cpu.time"]: - self._runtime_cpu_time_labels["type"] = metric - observer.observe( - getattr(proc_cpu, metric), self._runtime_cpu_time_labels, - ) + if hasattr(proc_cpu, metric): + self._runtime_cpu_time_labels["type"] = metric + observer.observe( + getattr(proc_cpu, metric), self._runtime_cpu_time_labels, + ) def _get_runtime_gc_count(self, observer: metrics.SumObserver) -> None: """Observer callback for garbage collection From 999eda775559af9e3d62b86edd0e0289294da2b0 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Tue, 13 Oct 2020 14:38:09 -0400 Subject: [PATCH 9/9] chore: bump dev version (#1235) --- .../CHANGELOG.md | 4 ++++ .../opentelemetry-instrumentation-system-metrics/setup.cfg | 6 +++--- .../opentelemetry/instrumentation/system_metrics/version.py | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md index 7405cd91f..5f6ff0530 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## Version 0.14b0 + +Released 2020-10-13 + - Fix issue when specific metrics are not available in certain OS ([#1207](https://github.com/open-telemetry/opentelemetry-python/pull/1207)) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg index 345393ac8..750b5e07e 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg @@ -39,13 +39,13 @@ package_dir= =src packages=find_namespace: install_requires = - opentelemetry-api == 0.14.dev0 - opentelemetry-sdk == 0.14.dev0 + opentelemetry-api == 0.15.dev0 + opentelemetry-sdk == 0.15.dev0 psutil ~= 5.7.0 [options.extras_require] test = - opentelemetry-test == 0.14.dev0 + opentelemetry-test == 0.15.dev0 [options.packages.find] where = src diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py index 0f9902789..e7b342d64 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.14.dev0" +__version__ = "0.15.dev0"