From 12e3fffda836c83c704e69bfe1d20c65ec1bc046 Mon Sep 17 00:00:00 2001 From: pridhi Date: Sun, 15 Jan 2023 21:48:58 +0530 Subject: [PATCH 01/32] Add instrumenation for threading Signed-off-by: pridhi --- .../LICENSE | 201 ++++++++++++++++++ .../README.rst | 23 ++ .../pyproject.toml | 58 +++++ .../instrumentation/threading/__init__.py | 68 ++++++ .../instrumentation/threading/package.py | 18 ++ .../instrumentation/threading/version.py | 15 ++ 6 files changed, 383 insertions(+) create mode 100644 instrumentation/opentelemetry-instrumentation-threading/LICENSE create mode 100644 instrumentation/opentelemetry-instrumentation-threading/README.rst create mode 100644 instrumentation/opentelemetry-instrumentation-threading/pyproject.toml create mode 100644 instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py create mode 100644 instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py create mode 100644 instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/version.py diff --git a/instrumentation/opentelemetry-instrumentation-threading/LICENSE b/instrumentation/opentelemetry-instrumentation-threading/LICENSE new file mode 100644 index 0000000000..1ef7dad2c5 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-threading/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 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. diff --git a/instrumentation/opentelemetry-instrumentation-threading/README.rst b/instrumentation/opentelemetry-instrumentation-threading/README.rst new file mode 100644 index 0000000000..895e2d3e77 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-threading/README.rst @@ -0,0 +1,23 @@ +OpenTelemetry Threading Tracing +=========================== + +|pypi| + +# To-Do : Whats is the image for opentelemetry-instrumentation-threading? +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-flask.svg + :target: https://pypi.org/project/opentelemetry-instrumentation-threading/ + + +Installation +------------ + +:: + + pip install opentelemetry-instrumentation-threading + +References +---------- + +* `OpenTelemetry Flask Instrumentation `_ +* `OpenTelemetry Project `_ +* `OpenTelemetry Python Examples `_ diff --git a/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml b/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml new file mode 100644 index 0000000000..db78a51410 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml @@ -0,0 +1,58 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "opentelemetry-instrumentation-threading" +dynamic = ["version"] +description = "Threading instrumentation for OpenTelemetry" +readme = "README.rst" +license = "Apache-2.0" +requires-python = ">=3.7" +authors = [ + { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, +] +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.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] +dependencies = [ + "opentelemetry-api ~= 1.12", + "opentelemetry-instrumentation == 0.37b0.dev", +] + +[project.optional-dependencies] +instruments = [ + "Threading >= 1.0, < 3.0", +] +test = [ + "opentelemetry-instrumentation-threading[instruments]", + "markupsafe==2.0.1", + "opentelemetry-test-utils == 0.37b0.dev", +] + +[project.entry-points.opentelemetry_instrumentor] +threading = "opentelemetry.instrumentation.threading:ThreadingInstrumentor" + +[project.urls] +Homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-threading" + +[tool.hatch.version] +path = "src/opentelemetry/instrumentation/threading/version.py" + +[tool.hatch.build.targets.sdist] +include = [ + "/src", + "/tests", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/opentelemetry"] diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py new file mode 100644 index 0000000000..c176fcf773 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py @@ -0,0 +1,68 @@ +# 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. + +# pylint: disable=empty-docstring,no-value-for-parameter,no-member,no-name-in-module + +import threading # pylint: disable=import-self +from os import environ +from typing import Collection + +from opentelemetry.instrumentation.instrumentor import BaseInstrumentor + +from opentelemetry.instrumentation.threading.package import _instruments +from opentelemetry.instrumentation.threading.version import __version__ + +from opentelemetry.trace import ( + INVALID_SPAN, + INVALID_SPAN_CONTEXT, + get_current_span, + get_tracer_provider, + get_tracer, + SpanKind +) + +ATTRIBUTE_THREAD_NAME = "currentthread.name" +DEFAULT_THREAD_NAME = "thread" + +class ThreadingInstrumentor(BaseInstrumentor): # pylint: disable=empty-docstring + + def instrumentation_dependencies(self) -> Collection[str]: + return _instruments + + def _instrument(self, **kwargs): + + tracer_provider = kwargs.get("tracer_provider", None) or get_tracer_provider() + tracer = get_tracer(__name__, __version__, tracer_provider) + start_func = getattr(threading.Thread, "start") + setattr( + threading.Thread, start_func.__name__, wrap_threading_start(start_func, tracer) + ) + +def wrap_threading_start(start_func, tracer): + """Wrap the start function from thread. Put the tracer information in the + threading object. + """ + + def call(self): + with tracer.start_as_current_span( + "thread.start", + kind=SpanKind.INTERNAL, + ) as span: + if span.is_recording(): + thread_name = start_func.__name__ or DEFAULT_THREAD_NAME + span.setr_attribute(ATTRIBUTE_THREAD_NAME, thread_name) + + return start_func(self) + + return call diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py new file mode 100644 index 0000000000..4847e2b93d --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py @@ -0,0 +1,18 @@ +# 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. + +#To-do : what is the actual version of threading? +_instruments = ("threading >= 1.0, < 3.0",) + +_supports_metrics = True diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/version.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/version.py new file mode 100644 index 0000000000..0a47df4f5a --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/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.37b0.dev" From 3d1ec3a3dfb329aaff2373e652d7d019dac2eb22 Mon Sep 17 00:00:00 2001 From: pridhi Date: Mon, 16 Jan 2023 23:21:34 +0530 Subject: [PATCH 02/32] Added tests for threading instrumentation Signed-off-by: pridhi --- .../tests/__init__.py | 0 .../tests/test_threading.py | 56 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 instrumentation/opentelemetry-instrumentation-threading/tests/__init__.py create mode 100644 instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-threading/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py new file mode 100644 index 0000000000..14600ed938 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -0,0 +1,56 @@ +# 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 threading +from unittest import mock + +from packaging import version + +from opentelemetry import trace as trace_api +from opentelemetry.instrumentation.threading import ThreadingInstrumentor +from opentelemetry.test.test_base import TestBase +from opentelemetry.trace import get_tracer + +#TEST_DIR = os.path.dirname(os.path.realpath(__file__)) +#TEST_DIR = os.path.join(TEST_DIR, "templates") + + +class TestThreadingInstrumentor(TestBase): + def setUp(self): + super().setUp() + ThreadingInstrumentor().instrument() + + self.tracer = get_tracer(__name__) + + #def tearDown(self): + #super().tearDown() + #ThreadingInstrumentor().uninstrument() + + def test_thread_with_root(self): + with self.tracer.start_As_current_span("root"): + t1 = threading.Thread.start_func() + self.assertEqual(t1.__dict__, "") + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 3) + + #pylint:disable = unbalanced-tuple-unpacking + render, template, root = spans[:3] + self.assertIs(render.parent, root.get_span_context()) + self.assertIs(template.parent, root.get_span_context()) + self.assertIsNone(root.parent) + + + From bcfe04a82ff74f518df38e1c11318943efabee57 Mon Sep 17 00:00:00 2001 From: pridhi Date: Tue, 17 Jan 2023 16:20:43 +0530 Subject: [PATCH 03/32] Updated tox.ini for threading module --- tox.ini | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tox.ini b/tox.ini index 3d73d37667..f8ff1ef568 100644 --- a/tox.ini +++ b/tox.ini @@ -181,6 +181,9 @@ envlist = py3{6,7,8,9,10,11}-test-instrumentation-system-metrics ; instrumentation-system-metrics intentionally excluded from pypy3 + ; opentelemetry-instrumentation-threading + py3{6,7,8,9,10,11}-test-instrumentation-threading + ; opentelemetry-instrumentation-tornado py3{7,8,9,10,11}-test-instrumentation-tornado pypy3-test-instrumentation-tornado @@ -320,6 +323,7 @@ changedir = test-instrumentation-sqlite3: instrumentation/opentelemetry-instrumentation-sqlite3/tests test-instrumentation-starlette: instrumentation/opentelemetry-instrumentation-starlette/tests test-instrumentation-system-metrics: instrumentation/opentelemetry-instrumentation-system-metrics/tests + test-instrumentation-threading: instrumentation/opentelemetry-instrumentation-threading/tests test-instrumentation-tornado: instrumentation/opentelemetry-instrumentation-tornado/tests test-instrumentation-tortoiseorm: instrumentation/opentelemetry-instrumentation-tortoiseorm/tests test-instrumentation-wsgi: instrumentation/opentelemetry-instrumentation-wsgi/tests @@ -409,6 +413,8 @@ commands_pre = system-metrics: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-system-metrics[test] + threading: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-threading[test] + tornado: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-tornado[test] tortoiseorm: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-tortoiseorm[test] @@ -539,6 +545,7 @@ commands_pre = python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx[test] python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-aws-lambda[test] python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-system-metrics[test] + python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-threading[test] python -m pip install -e {toxinidir}/exporter/opentelemetry-exporter-richconsole[test] python -m pip install -e {toxinidir}/exporter/opentelemetry-exporter-prometheus-remote-write[test] python -m pip install -e {toxinidir}/sdk-extension/opentelemetry-sdk-extension-aws[test] From 46c53944dd23f0a4d0af1b89cdb78287bbb5e70b Mon Sep 17 00:00:00 2001 From: pridhi Date: Wed, 18 Jan 2023 21:35:10 +0530 Subject: [PATCH 04/32] Removed instrumentation dependencies --- .../pyproject.toml | 4 ---- .../opentelemetry/instrumentation/threading/__init__.py | 8 ++++---- .../opentelemetry/instrumentation/threading/package.py | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml b/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml index db78a51410..b80e027fef 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml @@ -29,10 +29,6 @@ dependencies = [ "opentelemetry-instrumentation == 0.37b0.dev", ] -[project.optional-dependencies] -instruments = [ - "Threading >= 1.0, < 3.0", -] test = [ "opentelemetry-instrumentation-threading[instruments]", "markupsafe==2.0.1", diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py index c176fcf773..bc5c5a9bf4 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py @@ -20,7 +20,7 @@ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.threading.package import _instruments +#from opentelemetry.instrumentation.threading.package import _instruments from opentelemetry.instrumentation.threading.version import __version__ from opentelemetry.trace import ( @@ -37,8 +37,8 @@ class ThreadingInstrumentor(BaseInstrumentor): # pylint: disable=empty-docstring - def instrumentation_dependencies(self) -> Collection[str]: - return _instruments + # def instrumentation_dependencies(self) -> Collection[str]: + # return _instruments def _instrument(self, **kwargs): @@ -53,7 +53,7 @@ def wrap_threading_start(start_func, tracer): """Wrap the start function from thread. Put the tracer information in the threading object. """ - + def call(self): with tracer.start_as_current_span( "thread.start", diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py index 4847e2b93d..dd82eebafd 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py @@ -13,6 +13,6 @@ # limitations under the License. #To-do : what is the actual version of threading? -_instruments = ("threading >= 1.0, < 3.0",) +#_instruments = ("threading >= 1.0, < 3.0",) _supports_metrics = True From 5456f9bc69607b7ee6d60c0ec8a46d380d2b3a55 Mon Sep 17 00:00:00 2001 From: pridhi Date: Thu, 19 Jan 2023 22:39:46 +0530 Subject: [PATCH 05/32] Updated test file --- .../instrumentation/threading/__init__.py | 14 ++++-- .../instrumentation/threading/package.py | 2 +- .../tests/test_threading.py | 45 ++++++++++++------- 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py index bc5c5a9bf4..1c606b404c 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py @@ -20,7 +20,7 @@ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -#from opentelemetry.instrumentation.threading.package import _instruments +from opentelemetry.instrumentation.threading.package import _instruments from opentelemetry.instrumentation.threading.version import __version__ from opentelemetry.trace import ( @@ -37,18 +37,26 @@ class ThreadingInstrumentor(BaseInstrumentor): # pylint: disable=empty-docstring - # def instrumentation_dependencies(self) -> Collection[str]: - # return _instruments + start_func = None + + def instrumentation_dependencies(self) -> Collection[str]: + return _instruments def _instrument(self, **kwargs): tracer_provider = kwargs.get("tracer_provider", None) or get_tracer_provider() tracer = get_tracer(__name__, __version__, tracer_provider) start_func = getattr(threading.Thread, "start") + self.start_func = start_func setattr( threading.Thread, start_func.__name__, wrap_threading_start(start_func, tracer) ) + def _uninstrument(self, **kwargs): + setattr( + threading.Thread, self.start_func.__name__, self.start_func + ) + def wrap_threading_start(start_func, tracer): """Wrap the start function from thread. Put the tracer information in the threading object. diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py index dd82eebafd..32aeb3ee94 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py @@ -13,6 +13,6 @@ # limitations under the License. #To-do : what is the actual version of threading? -#_instruments = ("threading >= 1.0, < 3.0",) +_instruments = () _supports_metrics = True diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py index 14600ed938..f7fed9052a 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -34,23 +34,38 @@ def setUp(self): self.tracer = get_tracer(__name__) - #def tearDown(self): - #super().tearDown() - #ThreadingInstrumentor().uninstrument() + def tearDown(self): + super().tearDown() + ThreadingInstrumentor().uninstrument() def test_thread_with_root(self): - with self.tracer.start_As_current_span("root"): - t1 = threading.Thread.start_func() - self.assertEqual(t1.__dict__, "") - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 3) - - #pylint:disable = unbalanced-tuple-unpacking - render, template, root = spans[:3] - self.assertIs(render.parent, root.get_span_context()) - self.assertIs(template.parent, root.get_span_context()) - self.assertIsNone(root.parent) + mock_wrap_start = mock.Mock() + + mock_threading = mock.Mock() + + wrap_start_result = 'wrap start result' + + mock_wrap_start.return_value = wrap_start_result + + mock_start_func = mock.Mock() + + mock_start_func.__name__ = 'start' + + setattr(mock_threading.Thread, 'start', mock_start_func) + + patch_wrap_start = mock.patch( + 'opentelemetry.instrumentation.threading.wrap_threading_start', + mock_wrap_start) + + patch_threading = mock.patch( + 'opentelemetry.instrumentation.threading', mock_threading) + + with patch_wrap_start, \ + patch_threading: + ThreadingInstrumentor() + + self.assertEqual( + getattr(mock_threading.Thread, 'start'), wrap_start_result) From fb8df3c2015ffc268e01f6fc3be5972d244babbe Mon Sep 17 00:00:00 2001 From: pridhi Date: Thu, 19 Jan 2023 22:44:26 +0530 Subject: [PATCH 06/32] Updated README --- .../opentelemetry-instrumentation-threading/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/README.rst b/instrumentation/opentelemetry-instrumentation-threading/README.rst index 895e2d3e77..d741436ef3 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/README.rst +++ b/instrumentation/opentelemetry-instrumentation-threading/README.rst @@ -4,7 +4,7 @@ OpenTelemetry Threading Tracing |pypi| # To-Do : Whats is the image for opentelemetry-instrumentation-threading? -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-flask.svg +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-threading.svg :target: https://pypi.org/project/opentelemetry-instrumentation-threading/ From ca8b114ab09a582bc0c5b1f98f6d3876bec92152 Mon Sep 17 00:00:00 2001 From: pridhi Date: Fri, 20 Jan 2023 22:43:06 +0530 Subject: [PATCH 07/32] Added comments to test --- .../tests/test_threading.py | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py index f7fed9052a..0937c8488d 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -23,49 +23,61 @@ from opentelemetry.test.test_base import TestBase from opentelemetry.trace import get_tracer -#TEST_DIR = os.path.dirname(os.path.realpath(__file__)) -#TEST_DIR = os.path.join(TEST_DIR, "templates") - class TestThreadingInstrumentor(TestBase): - def setUp(self): - super().setUp() - ThreadingInstrumentor().instrument() + # def setUp(self): + # super().setUp() + # ThreadingInstrumentor().instrument() - self.tracer = get_tracer(__name__) + # self.tracer = get_tracer(__name__) def tearDown(self): super().tearDown() ThreadingInstrumentor().uninstrument() def test_thread_with_root(self): + # Initialized the mock function for + # opentelemetry.instrumentation.threading.wrap_threading_start mock_wrap_start = mock.Mock() + # Initialized the mock function for + # threading library imported in + # opentelemetry.instrumentation.threading mock_threading = mock.Mock() + #Sets variable for the return value for + # opentelemetry.instrumentation.threading.wrap_threading_start wrap_start_result = 'wrap start result' + #Sets return value for + # opentelemetry.instrumentation.threading.wrap_threading_start mock_wrap_start.return_value = wrap_start_result + #Initializes the mock function for threading.start mock_start_func = mock.Mock() + #sets the name of the mock(threading.start) function mock_start_func.__name__ = 'start' - + + #monkeypatches the mock_threading modules start function to the previoulyy created + #mock of start function setattr(mock_threading.Thread, 'start', mock_start_func) + #patches the opentelemetry.instrumentation.threading.wrap_threading_start patch_wrap_start = mock.patch( 'opentelemetry.instrumentation.threading.wrap_threading_start', mock_wrap_start) - + # patches the opentelemetry.instrumentation.threading.threading patch_threading = mock.patch( - 'opentelemetry.instrumentation.threading', mock_threading) - - with patch_wrap_start, \ - patch_threading: - ThreadingInstrumentor() - + 'opentelemetry.instrumentation.threading', mock_threading) + + #using the above two patches created, it calls the threadingInstrumentor class + with patch_wrap_start, patch_threading: + ThreadingInstrumentor().instrument() + print("********************************") + # print(mock_threading.Thread.start()) + + #checks if mock_wrap_start was called + self.assertFalse(mock_wrap_start.called) self.assertEqual( getattr(mock_threading.Thread, 'start'), wrap_start_result) - - - From c8d8f691ca23d9bc96174433983451f7506cc412 Mon Sep 17 00:00:00 2001 From: pridhi Date: Fri, 27 Jan 2023 16:17:24 +0530 Subject: [PATCH 08/32] Updated init and test files --- .../instrumentation/threading/__init__.py | 79 ++++++++++++------- .../tests/test_threading.py | 69 +++++----------- 2 files changed, 70 insertions(+), 78 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py index 1c606b404c..2a60c07171 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py @@ -14,9 +14,13 @@ # pylint: disable=empty-docstring,no-value-for-parameter,no-member,no-name-in-module +from concurrent.futures import thread import threading # pylint: disable=import-self from os import environ from typing import Collection +from opentelemetry import context +from wrapt import wrap_function_wrapper as _wrap + from opentelemetry.instrumentation.instrumentor import BaseInstrumentor @@ -35,6 +39,45 @@ ATTRIBUTE_THREAD_NAME = "currentthread.name" DEFAULT_THREAD_NAME = "thread" +def _with_tracer_wrapper(func): + """Helper for providing tracer for wrapper functions.""" + + def _with_tracer(tracer): + def wrapper(wrapped, instance, args, kwargs): + return func(tracer, wrapped, instance, args, kwargs) + + return wrapper + + return _with_tracer + +def _wrap_target(ctx, target_func, tracer): + """Helper for providing tracer for wrapper functions.""" + context.attach(ctx) + with tracer.start_as_current_span( + "threading.Thread.target", + kind=SpanKind.INTERNAL, + ) as span: + if span.is_recording(): + span.set_attribute(ATTRIBUTE_THREAD_NAME, target_func.__name__) + return target_func + +@_with_tracer_wrapper +def _wrap_thread(tracer, wrapped, instance, args, kwargs): + """Wrap `Threading.thread`""" + + target_func = kwargs.get("target") + + with tracer.start_as_current_span( + "threading.Thread", + kind=SpanKind.INTERNAL, + ) as span: + if span.is_recording(): + ctx = context.get_current() + # _wrap(target, "Thread", _wrap_target(tracer)) + kwargs["target"] = _wrap_target(ctx, target_func, tracer) + span.set_attribute(ATTRIBUTE_THREAD_NAME, wrapped.__name__) + return wrapped(*args, **kwargs) + class ThreadingInstrumentor(BaseInstrumentor): # pylint: disable=empty-docstring start_func = None @@ -42,35 +85,17 @@ class ThreadingInstrumentor(BaseInstrumentor): # pylint: disable=empty-docstrin def instrumentation_dependencies(self) -> Collection[str]: return _instruments - def _instrument(self, **kwargs): + + def _instrument(self, *args, **kwargs): tracer_provider = kwargs.get("tracer_provider", None) or get_tracer_provider() - tracer = get_tracer(__name__, __version__, tracer_provider) - start_func = getattr(threading.Thread, "start") - self.start_func = start_func - setattr( - threading.Thread, start_func.__name__, wrap_threading_start(start_func, tracer) - ) + tracer = get_tracer(__name__, __version__, tracer_provider) + _wrap(threading, "Thread", _wrap_thread(tracer)) + + def _uninstrument(self, **kwargs): - setattr( - threading.Thread, self.start_func.__name__, self.start_func - ) - -def wrap_threading_start(start_func, tracer): - """Wrap the start function from thread. Put the tracer information in the - threading object. - """ - - def call(self): - with tracer.start_as_current_span( - "thread.start", - kind=SpanKind.INTERNAL, - ) as span: - if span.is_recording(): - thread_name = start_func.__name__ or DEFAULT_THREAD_NAME - span.setr_attribute(ATTRIBUTE_THREAD_NAME, thread_name) - - return start_func(self) - return call + setattr( + threading.Thread, self.join_func.__name__, self.join_func + ) \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py index 0937c8488d..34e68b5d5e 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -17,67 +17,34 @@ from unittest import mock from packaging import version - from opentelemetry import trace as trace_api from opentelemetry.instrumentation.threading import ThreadingInstrumentor from opentelemetry.test.test_base import TestBase -from opentelemetry.trace import get_tracer +from opentelemetry.trace import SpanKind, get_tracer + class TestThreadingInstrumentor(TestBase): - # def setUp(self): - # super().setUp() - # ThreadingInstrumentor().instrument() + def setUp(self): + super().setUp() + ThreadingInstrumentor().instrument() - # self.tracer = get_tracer(__name__) + self.tracer = get_tracer(__name__) def tearDown(self): super().tearDown() - ThreadingInstrumentor().uninstrument() + # ThreadingInstrumentor().uninstrument() + def print_square(self, num): + print("Square: {}" .format(num * num)) def test_thread_with_root(self): - # Initialized the mock function for - # opentelemetry.instrumentation.threading.wrap_threading_start - mock_wrap_start = mock.Mock() - - # Initialized the mock function for - # threading library imported in - # opentelemetry.instrumentation.threading - mock_threading = mock.Mock() - - #Sets variable for the return value for - # opentelemetry.instrumentation.threading.wrap_threading_start - wrap_start_result = 'wrap start result' - - #Sets return value for - # opentelemetry.instrumentation.threading.wrap_threading_start - mock_wrap_start.return_value = wrap_start_result - - #Initializes the mock function for threading.start - mock_start_func = mock.Mock() - - #sets the name of the mock(threading.start) function - mock_start_func.__name__ = 'start' + + t1 = threading.Thread(target=self.print_square, args=(10)) + t1.start() + t1.join() + + spans = self.memory_exporter.get_finished_spans() - #monkeypatches the mock_threading modules start function to the previoulyy created - #mock of start function - setattr(mock_threading.Thread, 'start', mock_start_func) - - #patches the opentelemetry.instrumentation.threading.wrap_threading_start - patch_wrap_start = mock.patch( - 'opentelemetry.instrumentation.threading.wrap_threading_start', - mock_wrap_start) - # patches the opentelemetry.instrumentation.threading.threading - patch_threading = mock.patch( - 'opentelemetry.instrumentation.threading', mock_threading) - - #using the above two patches created, it calls the threadingInstrumentor class - with patch_wrap_start, patch_threading: - ThreadingInstrumentor().instrument() - print("********************************") - # print(mock_threading.Thread.start()) - - #checks if mock_wrap_start was called - self.assertFalse(mock_wrap_start.called) - self.assertEqual( - getattr(mock_threading.Thread, 'start'), wrap_start_result) + print(spans[0].__dict__) + print(spans[1].__dict__) + self.assertEqual(len(spans), 3) \ No newline at end of file From dcd388b5cf824f261debf7bb6dd40b73e72f1ba6 Mon Sep 17 00:00:00 2001 From: pridhi Date: Wed, 1 Feb 2023 22:46:44 +0530 Subject: [PATCH 09/32] Modified test file --- .../tests/test_threading.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py index 34e68b5d5e..6684dd9657 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -38,13 +38,15 @@ def print_square(self, num): print("Square: {}" .format(num * num)) def test_thread_with_root(self): - - t1 = threading.Thread(target=self.print_square, args=(10)) - t1.start() - t1.join() - + with self.tracer.start_as_current_span("root"): + t1 = threading.Thread(target=self.print_square, args=(10)) + t1.start() + t1.join() + spans = self.memory_exporter.get_finished_spans() - - print(spans[0].__dict__) - print(spans[1].__dict__) - self.assertEqual(len(spans), 3) \ No newline at end of file + self.assertEqual(len(spans), 3) + + target, thread, root = spans[:3] + self.assertIs(target.parent, thread.get_span_context()) + self.assertIs(thread.parent, root.get_span_context()) + self.assertIsNone(root.parent) From 9e3b007a1feed8f39df816c6e2367efd22e0efbf Mon Sep 17 00:00:00 2001 From: pridhi-arora <110390842+pridhi-arora@users.noreply.github.com> Date: Thu, 2 Feb 2023 19:09:03 +0530 Subject: [PATCH 10/32] Update instrumentation/opentelemetry-instrumentation-threading/README.rst Co-authored-by: Srikanth Chekuri --- .../opentelemetry-instrumentation-threading/README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/README.rst b/instrumentation/opentelemetry-instrumentation-threading/README.rst index d741436ef3..ee1bf4ebd9 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/README.rst +++ b/instrumentation/opentelemetry-instrumentation-threading/README.rst @@ -1,5 +1,5 @@ -OpenTelemetry Threading Tracing -=========================== +OpenTelemetry Threading Instrumentation +======================================= |pypi| From d3a2d9006ba7b451cb71480ed4afe1a0c6d8d402 Mon Sep 17 00:00:00 2001 From: pridhi-arora <110390842+pridhi-arora@users.noreply.github.com> Date: Thu, 2 Feb 2023 19:09:16 +0530 Subject: [PATCH 11/32] Update instrumentation/opentelemetry-instrumentation-threading/README.rst Co-authored-by: Srikanth Chekuri --- .../opentelemetry-instrumentation-threading/README.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/README.rst b/instrumentation/opentelemetry-instrumentation-threading/README.rst index ee1bf4ebd9..0a23b144bb 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/README.rst +++ b/instrumentation/opentelemetry-instrumentation-threading/README.rst @@ -3,7 +3,6 @@ OpenTelemetry Threading Instrumentation |pypi| -# To-Do : Whats is the image for opentelemetry-instrumentation-threading? .. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-threading.svg :target: https://pypi.org/project/opentelemetry-instrumentation-threading/ From c13a4b7e58d769d1159629efa1baf91ff650809c Mon Sep 17 00:00:00 2001 From: pridhi-arora <110390842+pridhi-arora@users.noreply.github.com> Date: Thu, 2 Feb 2023 19:10:44 +0530 Subject: [PATCH 12/32] Update instrumentation/opentelemetry-instrumentation-threading/README.rst Co-authored-by: Srikanth Chekuri --- .../opentelemetry-instrumentation-threading/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/README.rst b/instrumentation/opentelemetry-instrumentation-threading/README.rst index 0a23b144bb..505e1b56ed 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/README.rst +++ b/instrumentation/opentelemetry-instrumentation-threading/README.rst @@ -17,6 +17,6 @@ Installation References ---------- -* `OpenTelemetry Flask Instrumentation `_ +* `OpenTelemetry Threading Instrumentation `_ * `OpenTelemetry Project `_ * `OpenTelemetry Python Examples `_ From 4d5625f2f7a14da9ee87efbca7394bdee008409e Mon Sep 17 00:00:00 2001 From: pridhi-arora <110390842+pridhi-arora@users.noreply.github.com> Date: Thu, 2 Feb 2023 19:14:22 +0530 Subject: [PATCH 13/32] Update instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py Co-authored-by: Srikanth Chekuri --- .../src/opentelemetry/instrumentation/threading/package.py | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py index 32aeb3ee94..f61fd23a94 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -#To-do : what is the actual version of threading? _instruments = () _supports_metrics = True From 260c7f993a6049794f80fa046f1267445d9cffd8 Mon Sep 17 00:00:00 2001 From: pridhi Date: Thu, 2 Feb 2023 19:26:48 +0530 Subject: [PATCH 14/32] Updated according to review comments and added uninstrumentation code --- .../pyproject.toml | 1 - .../instrumentation/threading/__init__.py | 13 ++++++------- .../instrumentation/threading/package.py | 2 +- .../tests/test_threading.py | 11 +++++++++++ 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml b/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml index b80e027fef..4838acb699 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml @@ -31,7 +31,6 @@ dependencies = [ test = [ "opentelemetry-instrumentation-threading[instruments]", - "markupsafe==2.0.1", "opentelemetry-test-utils == 0.37b0.dev", ] diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py index 2a60c07171..4dee661003 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py @@ -20,7 +20,7 @@ from typing import Collection from opentelemetry import context from wrapt import wrap_function_wrapper as _wrap - +from opentelemetry.instrumentation.utils import unwrap from opentelemetry.instrumentation.instrumentor import BaseInstrumentor @@ -38,6 +38,8 @@ ATTRIBUTE_THREAD_NAME = "currentthread.name" DEFAULT_THREAD_NAME = "thread" +ATTRIBUTE_TARGET_NAME = "currenttarget.name" +DEFAULT_TARGET_NAME = "None" def _with_tracer_wrapper(func): """Helper for providing tracer for wrapper functions.""" @@ -58,7 +60,7 @@ def _wrap_target(ctx, target_func, tracer): kind=SpanKind.INTERNAL, ) as span: if span.is_recording(): - span.set_attribute(ATTRIBUTE_THREAD_NAME, target_func.__name__) + span.set_attribute(ATTRIBUTE_TARGET_NAME, target_func.__name__) return target_func @_with_tracer_wrapper @@ -73,7 +75,6 @@ def _wrap_thread(tracer, wrapped, instance, args, kwargs): ) as span: if span.is_recording(): ctx = context.get_current() - # _wrap(target, "Thread", _wrap_target(tracer)) kwargs["target"] = _wrap_target(ctx, target_func, tracer) span.set_attribute(ATTRIBUTE_THREAD_NAME, wrapped.__name__) return wrapped(*args, **kwargs) @@ -95,7 +96,5 @@ def _instrument(self, *args, **kwargs): def _uninstrument(self, **kwargs): - - setattr( - threading.Thread, self.join_func.__name__, self.join_func - ) \ No newline at end of file + unwrap(threading, "Thread") + \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py index f61fd23a94..1bf177779b 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py @@ -14,4 +14,4 @@ _instruments = () -_supports_metrics = True +_supports_metrics = False diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py index 6684dd9657..88c76c3e09 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -50,3 +50,14 @@ def test_thread_with_root(self): self.assertIs(target.parent, thread.get_span_context()) self.assertIs(thread.parent, root.get_span_context()) self.assertIsNone(root.parent) + + def test_uninstrumented(self): + ThreadingInstrumentor().uninstrument() + + t1 = threading.Thread(target=self.print_square, args=(10)) + t1.start() + t1.join() + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 0) + + ThreadingInstrumentor().instrument() \ No newline at end of file From aec3ff90747a02aa6fb339c1be18187e3e717602 Mon Sep 17 00:00:00 2001 From: Pridhi Arora Date: Tue, 7 Feb 2023 23:30:21 +0530 Subject: [PATCH 15/32] add instrumentor base class --- .../instrumentation/threading/__init__.py | 25 ++++++++++++++----- .../tests/test_threading.py | 12 ++++++--- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py index 4dee661003..1d73706494 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py @@ -14,7 +14,6 @@ # pylint: disable=empty-docstring,no-value-for-parameter,no-member,no-name-in-module -from concurrent.futures import thread import threading # pylint: disable=import-self from os import environ from typing import Collection @@ -27,6 +26,7 @@ from opentelemetry.instrumentation.threading.package import _instruments from opentelemetry.instrumentation.threading.version import __version__ +from opentelemetry import trace from opentelemetry.trace import ( INVALID_SPAN, INVALID_SPAN_CONTEXT, @@ -79,9 +79,23 @@ def _wrap_thread(tracer, wrapped, instance, args, kwargs): span.set_attribute(ATTRIBUTE_THREAD_NAME, wrapped.__name__) return wrapped(*args, **kwargs) +class _InstrumentedThread(threading.Thread): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._parent_span = None + + def start(self): + self._parent_span = get_current_span() + super().start() + + def run(self): + parent_span = self._parent_span or get_current_span() + trace.set_span_in_context(parent_span) + super().run() + class ThreadingInstrumentor(BaseInstrumentor): # pylint: disable=empty-docstring - start_func = None + original_threadcls = threading.Thread def instrumentation_dependencies(self) -> Collection[str]: return _instruments @@ -92,9 +106,8 @@ def _instrument(self, *args, **kwargs): tracer_provider = kwargs.get("tracer_provider", None) or get_tracer_provider() tracer = get_tracer(__name__, __version__, tracer_provider) - _wrap(threading, "Thread", _wrap_thread(tracer)) - + threading.Thread = _InstrumentedThread + _InstrumentedThread._tracer = tracer def _uninstrument(self, **kwargs): - unwrap(threading, "Thread") - \ No newline at end of file + threading.Thread = self.original_threadcls \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py index 88c76c3e09..2572567d95 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -35,18 +35,24 @@ def tearDown(self): super().tearDown() # ThreadingInstrumentor().uninstrument() def print_square(self, num): - print("Square: {}" .format(num * num)) + with self.tracer.start_as_current_span("target"): + print("Square: {}" .format(num * num)) def test_thread_with_root(self): + t1 = threading.Thread(target=self.print_square, args=(10)) + with self.tracer.start_as_current_span("root"): - t1 = threading.Thread(target=self.print_square, args=(10)) t1.start() t1.join() spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 3) + print(spans[0].__dict__) + print(spans[1].__dict__) + print(spans[2].__dict__) + self.assertEqual(len(spans), 2) target, thread, root = spans[:3] + self.assertIs(target.parent, thread.get_span_context()) self.assertIs(thread.parent, root.get_span_context()) self.assertIsNone(root.parent) From 5cc3e6f9f6441dbb47c1e267b68930409cac2461 Mon Sep 17 00:00:00 2001 From: pridhi Date: Fri, 10 Feb 2023 17:39:53 +0530 Subject: [PATCH 16/32] Fixed tests --- .../tests/test_threading.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py index 2572567d95..0f8fd213d9 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -39,7 +39,7 @@ def print_square(self, num): print("Square: {}" .format(num * num)) def test_thread_with_root(self): - t1 = threading.Thread(target=self.print_square, args=(10)) + t1 = threading.Thread(target=self.print_square, args=(10,)) with self.tracer.start_as_current_span("root"): t1.start() @@ -60,10 +60,10 @@ def test_thread_with_root(self): def test_uninstrumented(self): ThreadingInstrumentor().uninstrument() - t1 = threading.Thread(target=self.print_square, args=(10)) + t1 = threading.Thread(target=self.print_square, args=(10,)) t1.start() t1.join() spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 0) + self.assertEqual(len(spans), 1) ThreadingInstrumentor().instrument() \ No newline at end of file From bf2b5bb34190da1a9fd2b4bf4f87cbfc04e5d165 Mon Sep 17 00:00:00 2001 From: pridhi Date: Fri, 10 Feb 2023 22:30:38 +0530 Subject: [PATCH 17/32] Add context to run function and fix tests --- .../opentelemetry/instrumentation/threading/__init__.py | 2 ++ .../tests/test_threading.py | 9 ++------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py index 1d73706494..d88625035d 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py @@ -91,6 +91,8 @@ def start(self): def run(self): parent_span = self._parent_span or get_current_span() trace.set_span_in_context(parent_span) + ctx = trace.set_span_in_context(parent_span) + context.attach(ctx) super().run() class ThreadingInstrumentor(BaseInstrumentor): # pylint: disable=empty-docstring diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py index 0f8fd213d9..82802cc7e2 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -23,7 +23,6 @@ from opentelemetry.trace import SpanKind, get_tracer - class TestThreadingInstrumentor(TestBase): def setUp(self): super().setUp() @@ -46,15 +45,11 @@ def test_thread_with_root(self): t1.join() spans = self.memory_exporter.get_finished_spans() - print(spans[0].__dict__) - print(spans[1].__dict__) - print(spans[2].__dict__) self.assertEqual(len(spans), 2) - target, thread, root = spans[:3] + target, root = spans[:2] - self.assertIs(target.parent, thread.get_span_context()) - self.assertIs(thread.parent, root.get_span_context()) + self.assertIs(target.parent, root.get_span_context()) self.assertIsNone(root.parent) def test_uninstrumented(self): From 3704f6804f21d61428cb1f575e9af7941a421bf7 Mon Sep 17 00:00:00 2001 From: pridhi Date: Wed, 15 Feb 2023 13:39:22 +0530 Subject: [PATCH 18/32] Adds more tests --- .../tests/test_threading.py | 62 ++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py index 82802cc7e2..580e0bc92f 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -34,10 +34,32 @@ def tearDown(self): super().tearDown() # ThreadingInstrumentor().uninstrument() def print_square(self, num): - with self.tracer.start_as_current_span("target"): + with self.tracer.start_as_current_span("square"): print("Square: {}" .format(num * num)) - def test_thread_with_root(self): + def print_cube(self, num): + with self.tracer.start_as_current_span("cube"): + print("Cube: {}" .format(num * num * num)) + + def print_square_with_thread(self, num): + with self.tracer.start_as_current_span("square"): + t2 = threading.Thread(target=self.print_cube, args=(10,)) + print("Square: {}" .format(num * num)) + t2.start() + t2.join() + + def calculate(self, num): + with self.tracer.start_as_current_span("calculate"): + t1 = threading.Thread(target=self.print_square, args=(num,)) + t2 = threading.Thread(target=self.print_cube, args=(num,)) + t1.start() + t1.join() + + t2.start() + t2.join() + + + def test_without_thread_nesting(self): t1 = threading.Thread(target=self.print_square, args=(10,)) with self.tracer.start_as_current_span("root"): @@ -52,6 +74,42 @@ def test_thread_with_root(self): self.assertIs(target.parent, root.get_span_context()) self.assertIsNone(root.parent) + def test_with_thread_nesting(self): + t1 = threading.Thread(target=self.print_square_with_thread, args=(10,)) + + + with self.tracer.start_as_current_span("root"): + t1.start() + t1.join() + + spans = self.memory_exporter.get_finished_spans() + + self.assertEqual(len(spans), 3) + + cube, square, root = spans[:3] + + self.assertIs(cube.parent, square.get_span_context()) + self.assertIs(square.parent, root.get_span_context()) + self.assertIsNone(root.parent) + + def test_with_thread_multi_nesting(self): + t1 = threading.Thread(target=self.calculate, args=(10,)) + + with self.tracer.start_as_current_span("root"): + t1.start() + t1.join() + + spans = self.memory_exporter.get_finished_spans() + + self.assertEqual(len(spans), 4) + + cube, square, calculate, root = spans[:4] + + self.assertIs(cube.parent, calculate.get_span_context()) + self.assertIs(square.parent, calculate.get_span_context()) + self.assertIs(calculate.parent, root.get_span_context()) + self.assertIsNone(root.parent) + def test_uninstrumented(self): ThreadingInstrumentor().uninstrument() From 4c100ea438e1f5a4958904de8ca90cd719c267c6 Mon Sep 17 00:00:00 2001 From: pridhi-arora <110390842+pridhi-arora@users.noreply.github.com> Date: Thu, 16 Feb 2023 22:01:43 +0530 Subject: [PATCH 19/32] Update instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py Co-authored-by: Srikanth Chekuri --- .../tests/test_threading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py index 580e0bc92f..eefdf44598 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -32,7 +32,7 @@ def setUp(self): def tearDown(self): super().tearDown() - # ThreadingInstrumentor().uninstrument() + ThreadingInstrumentor().uninstrument() def print_square(self, num): with self.tracer.start_as_current_span("square"): print("Square: {}" .format(num * num)) From 2a38a50db3daa8ed74b348c72d09e868ab4ac7ff Mon Sep 17 00:00:00 2001 From: pridhi-arora <110390842+pridhi-arora@users.noreply.github.com> Date: Thu, 16 Feb 2023 22:19:58 +0530 Subject: [PATCH 20/32] Update instrumentation/opentelemetry-instrumentation-threading/pyproject.toml Co-authored-by: Srikanth Chekuri --- .../opentelemetry-instrumentation-threading/pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml b/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml index 4838acb699..86c8ff8844 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml @@ -29,6 +29,8 @@ dependencies = [ "opentelemetry-instrumentation == 0.37b0.dev", ] +[project.optional-dependencies] +instruments = [] test = [ "opentelemetry-instrumentation-threading[instruments]", "opentelemetry-test-utils == 0.37b0.dev", From 5e6bd840efd7e25bad34a5495aed0e2a00cef5d0 Mon Sep 17 00:00:00 2001 From: Pridhi Arora Date: Thu, 16 Feb 2023 22:33:34 +0530 Subject: [PATCH 21/32] code cleanup --- .../instrumentation/threading/__init__.py | 50 +------------------ 1 file changed, 1 insertion(+), 49 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py index d88625035d..cc0a2d826e 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py @@ -18,8 +18,6 @@ from os import environ from typing import Collection from opentelemetry import context -from wrapt import wrap_function_wrapper as _wrap -from opentelemetry.instrumentation.utils import unwrap from opentelemetry.instrumentation.instrumentor import BaseInstrumentor @@ -28,57 +26,11 @@ from opentelemetry import trace from opentelemetry.trace import ( - INVALID_SPAN, - INVALID_SPAN_CONTEXT, get_current_span, get_tracer_provider, - get_tracer, - SpanKind + get_tracer ) -ATTRIBUTE_THREAD_NAME = "currentthread.name" -DEFAULT_THREAD_NAME = "thread" -ATTRIBUTE_TARGET_NAME = "currenttarget.name" -DEFAULT_TARGET_NAME = "None" - -def _with_tracer_wrapper(func): - """Helper for providing tracer for wrapper functions.""" - - def _with_tracer(tracer): - def wrapper(wrapped, instance, args, kwargs): - return func(tracer, wrapped, instance, args, kwargs) - - return wrapper - - return _with_tracer - -def _wrap_target(ctx, target_func, tracer): - """Helper for providing tracer for wrapper functions.""" - context.attach(ctx) - with tracer.start_as_current_span( - "threading.Thread.target", - kind=SpanKind.INTERNAL, - ) as span: - if span.is_recording(): - span.set_attribute(ATTRIBUTE_TARGET_NAME, target_func.__name__) - return target_func - -@_with_tracer_wrapper -def _wrap_thread(tracer, wrapped, instance, args, kwargs): - """Wrap `Threading.thread`""" - - target_func = kwargs.get("target") - - with tracer.start_as_current_span( - "threading.Thread", - kind=SpanKind.INTERNAL, - ) as span: - if span.is_recording(): - ctx = context.get_current() - kwargs["target"] = _wrap_target(ctx, target_func, tracer) - span.set_attribute(ATTRIBUTE_THREAD_NAME, wrapped.__name__) - return wrapped(*args, **kwargs) - class _InstrumentedThread(threading.Thread): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) From c528784051d174497a9ef69a1076f1b81ea15f24 Mon Sep 17 00:00:00 2001 From: Pridhi Arora Date: Mon, 20 Feb 2023 21:49:11 +0530 Subject: [PATCH 22/32] Updated the versions --- .../pyproject.toml | 4 +- .../tests/test_threading.py | 42 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml b/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml index 86c8ff8844..86eaa6d999 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml @@ -26,14 +26,14 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.37b0.dev", + "opentelemetry-instrumentation == 0.38b0.dev", ] [project.optional-dependencies] instruments = [] test = [ "opentelemetry-instrumentation-threading[instruments]", - "opentelemetry-test-utils == 0.37b0.dev", + "opentelemetry-test-utils == 0.38b0.dev", ] [project.entry-points.opentelemetry_instrumentor] diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py index eefdf44598..1ba3594b00 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -43,28 +43,28 @@ def print_cube(self, num): def print_square_with_thread(self, num): with self.tracer.start_as_current_span("square"): - t2 = threading.Thread(target=self.print_cube, args=(10,)) + cube_thread = threading.Thread(target=self.print_cube, args=(10,)) print("Square: {}" .format(num * num)) - t2.start() - t2.join() + cube_thread.start() + cube_thread.join() def calculate(self, num): with self.tracer.start_as_current_span("calculate"): - t1 = threading.Thread(target=self.print_square, args=(num,)) - t2 = threading.Thread(target=self.print_cube, args=(num,)) - t1.start() - t1.join() + square_thread = threading.Thread(target=self.print_square, args=(num,)) + cube_thread = threading.Thread(target=self.print_cube, args=(num,)) + square_thread.start() + square_thread.join() - t2.start() - t2.join() + cube_thread.start() + cube_thread.join() def test_without_thread_nesting(self): - t1 = threading.Thread(target=self.print_square, args=(10,)) + square_thread = threading.Thread(target=self.print_square, args=(10,)) with self.tracer.start_as_current_span("root"): - t1.start() - t1.join() + square_thread.start() + square_thread.join() spans = self.memory_exporter.get_finished_spans() self.assertEqual(len(spans), 2) @@ -75,12 +75,12 @@ def test_without_thread_nesting(self): self.assertIsNone(root.parent) def test_with_thread_nesting(self): - t1 = threading.Thread(target=self.print_square_with_thread, args=(10,)) + square_thread = threading.Thread(target=self.print_square_with_thread, args=(10,)) with self.tracer.start_as_current_span("root"): - t1.start() - t1.join() + square_thread.start() + square_thread.join() spans = self.memory_exporter.get_finished_spans() @@ -93,11 +93,11 @@ def test_with_thread_nesting(self): self.assertIsNone(root.parent) def test_with_thread_multi_nesting(self): - t1 = threading.Thread(target=self.calculate, args=(10,)) + calculate_thread = threading.Thread(target=self.calculate, args=(10,)) with self.tracer.start_as_current_span("root"): - t1.start() - t1.join() + calculate_thread.start() + calculate_thread.join() spans = self.memory_exporter.get_finished_spans() @@ -113,9 +113,9 @@ def test_with_thread_multi_nesting(self): def test_uninstrumented(self): ThreadingInstrumentor().uninstrument() - t1 = threading.Thread(target=self.print_square, args=(10,)) - t1.start() - t1.join() + square_thread = threading.Thread(target=self.print_square, args=(10,)) + square_thread.start() + square_thread.join() spans = self.memory_exporter.get_finished_spans() self.assertEqual(len(spans), 1) From 075107c3adbbd7e6ca6f7d24a89313770e170807 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Mon, 20 Feb 2023 21:57:20 +0530 Subject: [PATCH 23/32] Update instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/version.py --- .../src/opentelemetry/instrumentation/threading/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/version.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/version.py index 0a47df4f5a..8778b43b17 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/version.py +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.37b0.dev" +__version__ = "0.38b0.dev" From 2f89aaa13da7727f138e83fe6c09af1862a1fa9e Mon Sep 17 00:00:00 2001 From: Pridhi Arora Date: Mon, 20 Feb 2023 21:57:59 +0530 Subject: [PATCH 24/32] Updated default dependency and project.toml --- opentelemetry-contrib-instrumentations/pyproject.toml | 1 + .../src/opentelemetry/instrumentation/bootstrap_gen.py | 1 + 2 files changed, 2 insertions(+) diff --git a/opentelemetry-contrib-instrumentations/pyproject.toml b/opentelemetry-contrib-instrumentations/pyproject.toml index a6f9132eaa..2a8b854071 100644 --- a/opentelemetry-contrib-instrumentations/pyproject.toml +++ b/opentelemetry-contrib-instrumentations/pyproject.toml @@ -66,6 +66,7 @@ dependencies = [ "opentelemetry-instrumentation-sqlite3==0.38b0.dev", "opentelemetry-instrumentation-starlette==0.38b0.dev", "opentelemetry-instrumentation-system-metrics==0.38b0.dev", + "opentelemetry-instrumentation-threading==0.38b0.dev", "opentelemetry-instrumentation-tornado==0.38b0.dev", "opentelemetry-instrumentation-tortoiseorm==0.38b0.dev", "opentelemetry-instrumentation-urllib==0.38b0.dev", diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py index cbebc5f203..985cd8877a 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py @@ -172,4 +172,5 @@ "opentelemetry-instrumentation-sqlite3==0.38b0.dev", "opentelemetry-instrumentation-urllib==0.38b0.dev", "opentelemetry-instrumentation-wsgi==0.38b0.dev", + "opentelemetry-instrumentation-threading==0.38b0.dev", ] From 2b22b2f094a305b389ec104caa59fabc1159870e Mon Sep 17 00:00:00 2001 From: Pridhi Arora Date: Mon, 20 Feb 2023 22:03:10 +0530 Subject: [PATCH 25/32] Added comments to test scenarios --- .../tests/test_threading.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py index 1ba3594b00..7bf0e91be8 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -75,6 +75,11 @@ def test_without_thread_nesting(self): self.assertIsNone(root.parent) def test_with_thread_nesting(self): + # + # Following scenario is tested. + # threadA -> methodA -> threadB -> methodB + # + square_thread = threading.Thread(target=self.print_square_with_thread, args=(10,)) @@ -93,6 +98,12 @@ def test_with_thread_nesting(self): self.assertIsNone(root.parent) def test_with_thread_multi_nesting(self): + # + # Following scenario is tested. + # / threadB -> methodB + # threadA -> methodA -> + # \ threadC -> methodC + # calculate_thread = threading.Thread(target=self.calculate, args=(10,)) with self.tracer.start_as_current_span("root"): From 147f3d4cc407bca759ad15c70e8b4578e7475e1a Mon Sep 17 00:00:00 2001 From: Pridhi Arora Date: Wed, 22 Feb 2023 15:57:41 +0530 Subject: [PATCH 26/32] Update CHANGELOG --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e8f981e0e..0219acf3d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-system-metrics` Fix initialization of the instrumentation class when configuration is provided ([#1438](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1439)) +### Added + +- `opentelemetry-instrumentation-threading` Add instrumentation for python threading module + ([#1582](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1582))**** + ## Version 1.16.0/0.37b0 (2023-02-17) ### Added From a70ea704fd21c5a43597ab85514d8b74d847edda Mon Sep 17 00:00:00 2001 From: Pridhi Arora Date: Thu, 2 Mar 2023 12:27:05 +0530 Subject: [PATCH 27/32] Fix all lint issues --- instrumentation/README.md | 1 + .../instrumentation/threading/__init__.py | 29 +++++---- .../tests/test_threading.py | 61 ++++++++++--------- .../instrumentation/bootstrap_gen.py | 2 +- 4 files changed, 48 insertions(+), 45 deletions(-) diff --git a/instrumentation/README.md b/instrumentation/README.md index fd3a0258cf..34ba365895 100644 --- a/instrumentation/README.md +++ b/instrumentation/README.md @@ -38,6 +38,7 @@ | [opentelemetry-instrumentation-sqlite3](./opentelemetry-instrumentation-sqlite3) | sqlite3 | No | [opentelemetry-instrumentation-starlette](./opentelemetry-instrumentation-starlette) | starlette ~= 0.13.0 | Yes | [opentelemetry-instrumentation-system-metrics](./opentelemetry-instrumentation-system-metrics) | psutil >= 5 | No +| [opentelemetry-instrumentation-threading](./opentelemetry-instrumentation-threading) | threading | No | [opentelemetry-instrumentation-tornado](./opentelemetry-instrumentation-tornado) | tornado >= 5.1.1 | Yes | [opentelemetry-instrumentation-tortoiseorm](./opentelemetry-instrumentation-tortoiseorm) | tortoise-orm >= 0.17.0 | No | [opentelemetry-instrumentation-urllib](./opentelemetry-instrumentation-urllib) | urllib | Yes diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py index cc0a2d826e..bbfd7acca3 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py @@ -14,23 +14,20 @@ # pylint: disable=empty-docstring,no-value-for-parameter,no-member,no-name-in-module -import threading # pylint: disable=import-self -from os import environ +import threading # pylint: disable=import-self from typing import Collection -from opentelemetry import context +from opentelemetry import context, trace from opentelemetry.instrumentation.instrumentor import BaseInstrumentor - from opentelemetry.instrumentation.threading.package import _instruments from opentelemetry.instrumentation.threading.version import __version__ - -from opentelemetry import trace from opentelemetry.trace import ( get_current_span, + get_tracer, get_tracer_provider, - get_tracer ) + class _InstrumentedThread(threading.Thread): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -47,21 +44,23 @@ def run(self): context.attach(ctx) super().run() -class ThreadingInstrumentor(BaseInstrumentor): # pylint: disable=empty-docstring - + +class ThreadingInstrumentor( + BaseInstrumentor +): # pylint: disable=empty-docstring original_threadcls = threading.Thread def instrumentation_dependencies(self) -> Collection[str]: return _instruments - - def _instrument(self, *args, **kwargs): - - tracer_provider = kwargs.get("tracer_provider", None) or get_tracer_provider() + def _instrument(self, *args, **kwargs): + tracer_provider = ( + kwargs.get("tracer_provider", None) or get_tracer_provider() + ) tracer = get_tracer(__name__, __version__, tracer_provider) threading.Thread = _InstrumentedThread _InstrumentedThread._tracer = tracer - + def _uninstrument(self, **kwargs): - threading.Thread = self.original_threadcls \ No newline at end of file + threading.Thread = self.original_threadcls diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py index 7bf0e91be8..c8de0c8690 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -1,3 +1,4 @@ +# pylint: disable=trailing-whitespace # Copyright The OpenTelemetry Authors # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,15 +13,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os import threading -from unittest import mock -from packaging import version -from opentelemetry import trace as trace_api from opentelemetry.instrumentation.threading import ThreadingInstrumentor from opentelemetry.test.test_base import TestBase -from opentelemetry.trace import SpanKind, get_tracer +from opentelemetry.trace import get_tracer class TestThreadingInstrumentor(TestBase): @@ -33,32 +30,35 @@ def setUp(self): def tearDown(self): super().tearDown() ThreadingInstrumentor().uninstrument() + def print_square(self, num): with self.tracer.start_as_current_span("square"): - print("Square: {}" .format(num * num)) + return num * num def print_cube(self, num): with self.tracer.start_as_current_span("cube"): - print("Cube: {}" .format(num * num * num)) + return num * num * num def print_square_with_thread(self, num): with self.tracer.start_as_current_span("square"): cube_thread = threading.Thread(target=self.print_cube, args=(10,)) - print("Square: {}" .format(num * num)) + cube_thread.start() cube_thread.join() + return num * num def calculate(self, num): with self.tracer.start_as_current_span("calculate"): - square_thread = threading.Thread(target=self.print_square, args=(num,)) + square_thread = threading.Thread( + target=self.print_square, args=(num,) + ) cube_thread = threading.Thread(target=self.print_cube, args=(num,)) square_thread.start() square_thread.join() - + cube_thread.start() cube_thread.join() - def test_without_thread_nesting(self): square_thread = threading.Thread(target=self.print_square, args=(10,)) @@ -69,19 +69,21 @@ def test_without_thread_nesting(self): spans = self.memory_exporter.get_finished_spans() self.assertEqual(len(spans), 2) + # pylint: disable=unbalanced-tuple-unpacking target, root = spans[:2] - + self.assertIs(target.parent, root.get_span_context()) self.assertIsNone(root.parent) def test_with_thread_nesting(self): - # - # Following scenario is tested. - # threadA -> methodA -> threadB -> methodB - # - - square_thread = threading.Thread(target=self.print_square_with_thread, args=(10,)) + # + # Following scenario is tested. + # threadA -> methodA -> threadB -> methodB + # + square_thread = threading.Thread( + target=self.print_square_with_thread, args=(10,) + ) with self.tracer.start_as_current_span("root"): square_thread.start() @@ -90,20 +92,20 @@ def test_with_thread_nesting(self): spans = self.memory_exporter.get_finished_spans() self.assertEqual(len(spans), 3) - + # pylint: disable=unbalanced-tuple-unpacking cube, square, root = spans[:3] - + self.assertIs(cube.parent, square.get_span_context()) self.assertIs(square.parent, root.get_span_context()) self.assertIsNone(root.parent) def test_with_thread_multi_nesting(self): - # - # Following scenario is tested. - # / threadB -> methodB - # threadA -> methodA -> - # \ threadC -> methodC - # + # + # Following scenario is tested. + # / threadB -> methodB + # threadA -> methodA -> + # \ threadC -> methodC + # calculate_thread = threading.Thread(target=self.calculate, args=(10,)) with self.tracer.start_as_current_span("root"): @@ -111,11 +113,12 @@ def test_with_thread_multi_nesting(self): calculate_thread.join() spans = self.memory_exporter.get_finished_spans() - + self.assertEqual(len(spans), 4) + # pylint: disable=unbalanced-tuple-unpacking cube, square, calculate, root = spans[:4] - + self.assertIs(cube.parent, calculate.get_span_context()) self.assertIs(square.parent, calculate.get_span_context()) self.assertIs(calculate.parent, root.get_span_context()) @@ -130,4 +133,4 @@ def test_uninstrumented(self): spans = self.memory_exporter.get_finished_spans() self.assertEqual(len(spans), 1) - ThreadingInstrumentor().instrument() \ No newline at end of file + ThreadingInstrumentor().instrument() diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py index 985cd8877a..3f5e67d539 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py @@ -170,7 +170,7 @@ "opentelemetry-instrumentation-dbapi==0.38b0.dev", "opentelemetry-instrumentation-logging==0.38b0.dev", "opentelemetry-instrumentation-sqlite3==0.38b0.dev", + "opentelemetry-instrumentation-threading==0.38b0.dev", "opentelemetry-instrumentation-urllib==0.38b0.dev", "opentelemetry-instrumentation-wsgi==0.38b0.dev", - "opentelemetry-instrumentation-threading==0.38b0.dev", ] From 971e86489524a2d8dc9e5f4ed7e98991f9b8a817 Mon Sep 17 00:00:00 2001 From: Pridhi Arora Date: Thu, 2 Mar 2023 13:15:32 +0530 Subject: [PATCH 28/32] Update CHANGELOG --- CHANGELOG.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0219acf3d6..38fdab89de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#1608](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1608)) - Add support for enabling Redis sanitization from environment variable ([#1690](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1690)) +- `opentelemetry-instrumentation-threading` Add instrumentation for python threading module + ([#1582](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1582))**** + ### Fixed @@ -27,10 +30,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-system-metrics` Fix initialization of the instrumentation class when configuration is provided ([#1438](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1439)) -### Added - -- `opentelemetry-instrumentation-threading` Add instrumentation for python threading module - ([#1582](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1582))**** ## Version 1.16.0/0.37b0 (2023-02-17) From 8f50ce47f1d9b555ed393cb56ebef7328832ccdc Mon Sep 17 00:00:00 2001 From: pridhi-arora <110390842+pridhi-arora@users.noreply.github.com> Date: Thu, 2 Mar 2023 14:54:36 +0530 Subject: [PATCH 29/32] Update CHANGELOG.md Co-authored-by: Srikanth Chekuri --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38fdab89de..c25ae1ff1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add support for enabling Redis sanitization from environment variable ([#1690](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1690)) - `opentelemetry-instrumentation-threading` Add instrumentation for python threading module - ([#1582](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1582))**** + ([#1582](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1582)) ### Fixed From 9315652dbcc78d974cd29db63f2656548324dbc5 Mon Sep 17 00:00:00 2001 From: pridhi-arora <110390842+pridhi-arora@users.noreply.github.com> Date: Thu, 2 Mar 2023 14:54:57 +0530 Subject: [PATCH 30/32] Update instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py Co-authored-by: Srikanth Chekuri --- .../src/opentelemetry/instrumentation/threading/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py index bbfd7acca3..d0431f57f9 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py @@ -39,7 +39,6 @@ def start(self): def run(self): parent_span = self._parent_span or get_current_span() - trace.set_span_in_context(parent_span) ctx = trace.set_span_in_context(parent_span) context.attach(ctx) super().run() From 2181cccf5f39ae6de793045e7eb5ed74a748d7c0 Mon Sep 17 00:00:00 2001 From: Pridhi Arora Date: Fri, 10 Mar 2023 16:13:35 +0530 Subject: [PATCH 31/32] Update Docs --- docs/instrumentation/threading/threading.rst | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 docs/instrumentation/threading/threading.rst diff --git a/docs/instrumentation/threading/threading.rst b/docs/instrumentation/threading/threading.rst new file mode 100644 index 0000000000..d4fad417b7 --- /dev/null +++ b/docs/instrumentation/threading/threading.rst @@ -0,0 +1,7 @@ +OpenTelemetry Threading Instrumentation +=================================== + +.. automodule:: opentelemetry.instrumentation.threading + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file From 22a396fa7486f02ef5946a1190bb564cfe7638c3 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 28 Jun 2023 16:40:55 +0200 Subject: [PATCH 32/32] Update docs/instrumentation/threading/threading.rst Co-authored-by: Srikanth Chekuri --- docs/instrumentation/threading/threading.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/instrumentation/threading/threading.rst b/docs/instrumentation/threading/threading.rst index d4fad417b7..a65066b893 100644 --- a/docs/instrumentation/threading/threading.rst +++ b/docs/instrumentation/threading/threading.rst @@ -1,5 +1,5 @@ OpenTelemetry Threading Instrumentation -=================================== +======================================= .. automodule:: opentelemetry.instrumentation.threading :members: