From fdb2e141d4455a367f9e5fb1971a950c6a59178a Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Tue, 9 Apr 2024 10:49:26 -0700 Subject: [PATCH 01/58] Update contrib repo approvers list (#2395) --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c315b4a876..6968de717e 100644 --- a/README.md +++ b/README.md @@ -108,14 +108,20 @@ Approvers ([@open-telemetry/python-approvers](https://github.com/orgs/open-telem - [Aaron Abbott](https://github.com/aabmass), Google - [Jeremy Voss](https://github.com/jeremydvoss), Microsoft +- [Owais Lone](https://github.com/owais), Splunk +- [Pablo Collins](https://github.com/pmcollins), Splunk +- [Riccardo Magliocchetti](https://github.com/xrmx), Elastic - [Sanket Mehta](https://github.com/sanketmehta28), Cisco +- [Srikanth Chekuri](https://github.com/srikanthccv), signoz.io +- [Tammy Baylis](https://github.com/tammy-baylis-swi), SolarWinds Emeritus Approvers: +- [Ashutosh Goel](https://github.com/ashu658), Cisco - [Héctor Hernández](https://github.com/hectorhdzg), Microsoft -- [Yusuke Tsutsumi](https://github.com/toumorokoshi), Google +- [Nikolay Sokolik](https://github.com/oxeye-nikolay), Oxeye +- [Nikolay Sokolik](https://github.com/nikosokolik), Oxeye - [Nathaniel Ruiz Nowell](https://github.com/NathanielRN), AWS -- [Ashutosh Goel](https://github.com/ashu658), Cisco *Find more about the approver role in [community repository](https://github.com/open-telemetry/community/blob/main/community-membership.md#approver).* @@ -129,7 +135,7 @@ Emeritus Maintainers: - [Alex Boten](https://github.com/codeboten), Lightstep - [Owais Lone](https://github.com/owais), Splunk -- [Srikanth Chekuri](https://github.com/srikanthccv), signoz.io +- [Yusuke Tsutsumi](https://github.com/toumorokoshi), Google *Find more about the maintainer role in [community repository](https://github.com/open-telemetry/community/blob/main/community-membership.md#maintainer).* From 804a9090a111d20b63338012318fd00ff6178cb0 Mon Sep 17 00:00:00 2001 From: abstractOwl Date: Wed, 10 Apr 2024 10:05:38 -0700 Subject: [PATCH 02/58] Add AWS resource detector entry points (#2382) --- CHANGELOG.md | 3 +++ .../opentelemetry-sdk-extension-aws/pyproject.toml | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c361881fd..e5186ac7fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +- `opentelemetry-sdk-extension-aws` Register AWS resource detectors under the + `opentelemetry_resource_detector` entry point + ([#2382](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2382)) ### Breaking changes diff --git a/sdk-extension/opentelemetry-sdk-extension-aws/pyproject.toml b/sdk-extension/opentelemetry-sdk-extension-aws/pyproject.toml index 52259bd52b..12b2b23ddc 100644 --- a/sdk-extension/opentelemetry-sdk-extension-aws/pyproject.toml +++ b/sdk-extension/opentelemetry-sdk-extension-aws/pyproject.toml @@ -30,6 +30,13 @@ dependencies = [ [project.entry-points.opentelemetry_id_generator] xray = "opentelemetry.sdk.extension.aws.trace.aws_xray_id_generator:AwsXRayIdGenerator" +[project.entry-points.opentelemetry_resource_detector] +aws_ec2 = "opentelemetry.sdk.extension.aws.resource.ec2:AwsEc2ResourceDetector" +aws_ecs = "opentelemetry.sdk.extension.aws.resource.ecs:AwsEcsResourceDetector" +aws_eks = "opentelemetry.sdk.extension.aws.resource.eks:AwsEksResourceDetector" +aws_elastic_beanstalk = "opentelemetry.sdk.extension.aws.resource.beanstalk:AwsBeanstalkResourceDetector" +aws_lambda = "opentelemetry.sdk.extension.aws.resource._lambda:AwsLambdaResourceDetector" + [project.urls] Homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/sdk-extension/opentelemetry-sdk-extension-aws" From a5c48871fa299d7a65d3b1e30989e624792ad4b1 Mon Sep 17 00:00:00 2001 From: Changemyminds Date: Fri, 12 Apr 2024 00:57:19 +0800 Subject: [PATCH 03/58] feat: add opentelemetry-instrumentation-threading library (#2253) * feat: add opentelemetry-instrumentation-threading library * fix: update python file with black formatter * fix: modified title underline too short issue * fix: modified library sorted via isort tool * fix: modified CHANGELOG.md and remove unused parameter * test: migrated unit test cases from the #1582 to this project * chroe: updated the tox.ini test commands * fix: fixed the lint issue * feat: support ThreadPool and update document * fix: fixed the lint issue * refactor: remove redundant class and simplify capture OTel context usage * fix: removed unused parameter * test: added a new test case for thread pool * fix: remove unused return response * refactor: compared the array * fix: remove f-string * fix: fixed pylint issue * fix: test library * fix: updated CHANGELOG.md --------- Co-authored-by: Aaron Abbott --- CHANGELOG.md | 5 + docs/instrumentation/threading/threading.rst | 7 + instrumentation/README.md | 1 + .../LICENSE | 201 ++++++++++++++++ .../README.rst | 25 ++ .../pyproject.toml | 51 ++++ .../instrumentation/threading/__init__.py | 149 ++++++++++++ .../instrumentation/threading/package.py | 17 ++ .../instrumentation/threading/version.py | 15 ++ .../test-requirements.txt | 18 ++ .../tests/__init__.py | 0 .../tests/test_threading.py | 226 ++++++++++++++++++ .../pyproject.toml | 1 + .../instrumentation/bootstrap_gen.py | 1 + tox.ini | 8 + 15 files changed, 725 insertions(+) create mode 100644 docs/instrumentation/threading/threading.rst 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 create mode 100644 instrumentation/opentelemetry-instrumentation-threading/test-requirements.txt create mode 100644 instrumentation/opentelemetry-instrumentation-threading/tests/__init__.py create mode 100644 instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py diff --git a/CHANGELOG.md b/CHANGELOG.md index e5186ac7fa..911ae8fa70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Rename `type` attribute to `asgi.event.type` in `opentelemetry-instrumentation-asgi` ([#2300](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2300)) +### Added + +- `opentelemetry-instrumentation-threading` Initial release for threading + ([#2253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2253)) + ## Version 1.24.0/0.45b0 (2024-03-28) ### Added diff --git a/docs/instrumentation/threading/threading.rst b/docs/instrumentation/threading/threading.rst new file mode 100644 index 0000000000..06bca89a49 --- /dev/null +++ b/docs/instrumentation/threading/threading.rst @@ -0,0 +1,7 @@ +OpenTelemetry Threading Instrumentation +======================================= + +.. automodule:: opentelemetry.instrumentation.threading + :members: + :undoc-members: + :show-inheritance: diff --git a/instrumentation/README.md b/instrumentation/README.md index 0cce7e5de7..62caabd81a 100644 --- a/instrumentation/README.md +++ b/instrumentation/README.md @@ -43,6 +43,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/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..93097dfcb6 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-threading/README.rst @@ -0,0 +1,25 @@ +OpenTelemetry threading Instrumentation +======================================= + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-threading.svg + :target: https://pypi.org/project/opentelemetry-instrumentation-threading/ + +This library provides instrumentation for the `threading` module to ensure that +the OpenTelemetry context is propagated across threads. It is important to note +that this instrumentation does not produce any telemetry data on its own. It +merely ensures that the context is correctly propagated when threads are used. + +Installation +------------ + +:: + + pip install opentelemetry-instrumentation-threading + +References +---------- + +* `OpenTelemetry Threading Tracing `_ +* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml b/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml new file mode 100644 index 0000000000..16874b13f9 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml @@ -0,0 +1,51 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "opentelemetry-instrumentation-threading" +dynamic = ["version"] +description = "Thread context propagation support for OpenTelemetry" +readme = "README.rst" +license = "Apache-2.0" +requires-python = ">=3.8" +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.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] +dependencies = [ + "opentelemetry-api ~= 1.12", + "opentelemetry-instrumentation == 0.46b0.dev", + "wrapt >= 1.0.0, < 2.0.0", +] + +[project.optional-dependencies] +instruments = [] + +[project.entry-points.opentelemetry_instrumentor] +threading = "opentelemetry.instrumentation.threading:ThreadingInstrumentor" + +[project.urls] +Homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/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..be1eec139e --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/__init__.py @@ -0,0 +1,149 @@ +# 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 threading to propagate OpenTelemetry context. + +Usage +----- + +.. code-block:: python + + from opentelemetry.instrumentation.threading import ThreadingInstrumentor + + ThreadingInstrumentor().instrument() + +This library provides instrumentation for the `threading` module to ensure that +the OpenTelemetry context is propagated across threads. It is important to note +that this instrumentation does not produce any telemetry data on its own. It +merely ensures that the context is correctly propagated when threads are used. + + +When instrumented, new threads created using threading.Thread, threading.Timer, +or within futures.ThreadPoolExecutor will have the current OpenTelemetry +context attached, and this context will be re-activated in the thread's +run method or the executor's worker thread." +""" + +import threading +from concurrent import futures +from typing import Collection + +from wrapt import wrap_function_wrapper + +from opentelemetry import context +from opentelemetry.instrumentation.instrumentor import BaseInstrumentor +from opentelemetry.instrumentation.threading.package import _instruments +from opentelemetry.instrumentation.utils import unwrap + + +class ThreadingInstrumentor(BaseInstrumentor): + __WRAPPER_START_METHOD = "start" + __WRAPPER_RUN_METHOD = "run" + __WRAPPER_SUBMIT_METHOD = "submit" + + def instrumentation_dependencies(self) -> Collection[str]: + return _instruments + + def _instrument(self, **kwargs): + self._instrument_thread() + self._instrument_timer() + self._instrument_thread_pool() + + def _uninstrument(self, **kwargs): + self._uninstrument_thread() + self._uninstrument_timer() + self._uninstrument_thread_pool() + + @staticmethod + def _instrument_thread(): + wrap_function_wrapper( + threading.Thread, + ThreadingInstrumentor.__WRAPPER_START_METHOD, + ThreadingInstrumentor.__wrap_threading_start, + ) + wrap_function_wrapper( + threading.Thread, + ThreadingInstrumentor.__WRAPPER_RUN_METHOD, + ThreadingInstrumentor.__wrap_threading_run, + ) + + @staticmethod + def _instrument_timer(): + wrap_function_wrapper( + threading.Timer, + ThreadingInstrumentor.__WRAPPER_START_METHOD, + ThreadingInstrumentor.__wrap_threading_start, + ) + wrap_function_wrapper( + threading.Timer, + ThreadingInstrumentor.__WRAPPER_RUN_METHOD, + ThreadingInstrumentor.__wrap_threading_run, + ) + + @staticmethod + def _instrument_thread_pool(): + wrap_function_wrapper( + futures.ThreadPoolExecutor, + ThreadingInstrumentor.__WRAPPER_SUBMIT_METHOD, + ThreadingInstrumentor.__wrap_thread_pool_submit, + ) + + @staticmethod + def _uninstrument_thread(): + unwrap(threading.Thread, ThreadingInstrumentor.__WRAPPER_START_METHOD) + unwrap(threading.Thread, ThreadingInstrumentor.__WRAPPER_RUN_METHOD) + + @staticmethod + def _uninstrument_timer(): + unwrap(threading.Timer, ThreadingInstrumentor.__WRAPPER_START_METHOD) + unwrap(threading.Timer, ThreadingInstrumentor.__WRAPPER_RUN_METHOD) + + @staticmethod + def _uninstrument_thread_pool(): + unwrap( + futures.ThreadPoolExecutor, + ThreadingInstrumentor.__WRAPPER_SUBMIT_METHOD, + ) + + @staticmethod + def __wrap_threading_start(call_wrapped, instance, args, kwargs): + instance._otel_context = context.get_current() + return call_wrapped(*args, **kwargs) + + @staticmethod + def __wrap_threading_run(call_wrapped, instance, args, kwargs): + token = None + try: + token = context.attach(instance._otel_context) + return call_wrapped(*args, **kwargs) + finally: + context.detach(token) + + @staticmethod + def __wrap_thread_pool_submit(call_wrapped, instance, args, kwargs): + # obtain the original function and wrapped kwargs + original_func = args[0] + otel_context = context.get_current() + + def wrapped_func(*func_args, **func_kwargs): + token = None + try: + token = context.attach(otel_context) + return original_func(*func_args, **func_kwargs) + finally: + context.detach(token) + + # replace the original function with the wrapped function + new_args = (wrapped_func,) + args[1:] + return call_wrapped(*new_args, **kwargs) 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..1bf177779b --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/package.py @@ -0,0 +1,17 @@ +# 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. + +_instruments = () + +_supports_metrics = False 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..ff4933b20b --- /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.46b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-threading/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-threading/test-requirements.txt new file mode 100644 index 0000000000..ffda21f234 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-threading/test-requirements.txt @@ -0,0 +1,18 @@ +asgiref==3.7.2 +attrs==23.2.0 +confluent-kafka==2.3.0 +Deprecated==1.2.14 +importlib-metadata==6.11.0 +iniconfig==2.0.0 +packaging==23.2 +pluggy==1.4.0 +py==1.11.0 +py-cpuinfo==9.0.0 +pytest==7.1.3 +pytest-benchmark==4.0.0 +tomli==2.0.1 +typing_extensions==4.9.0 +wrapt==1.16.0 +zipp==3.17.0 +-e opentelemetry-instrumentation +-e instrumentation/opentelemetry-instrumentation-threading 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..15f67b8d61 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -0,0 +1,226 @@ +# Copyright 2020, 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 threading +from concurrent.futures import ThreadPoolExecutor +from typing import List + +from opentelemetry import trace +from opentelemetry.instrumentation.threading import ThreadingInstrumentor +from opentelemetry.test.test_base import TestBase + + +class TestThreading(TestBase): + def setUp(self): + super().setUp() + self._tracer = self.tracer_provider.get_tracer(__name__) + self._mock_span_contexts: List[trace.SpanContext] = [] + ThreadingInstrumentor().instrument() + + def tearDown(self): + ThreadingInstrumentor().uninstrument() + super().tearDown() + + def get_root_span(self): + return self._tracer.start_as_current_span("rootSpan") + + def test_trace_context_propagation_in_thread(self): + self.run_threading_test(threading.Thread(target=self.fake_func)) + + def test_trace_context_propagation_in_timer(self): + self.run_threading_test( + threading.Timer(interval=1, function=self.fake_func) + ) + + def run_threading_test(self, thread: threading.Thread): + with self.get_root_span() as span: + expected_span_context = span.get_span_context() + thread.start() + thread.join() + + # check result + self.assertEqual(len(self._mock_span_contexts), 1) + self.assertEqual( + self._mock_span_contexts[0], expected_span_context + ) + + def test_trace_context_propagation_in_thread_pool_with_multiple_workers( + self, + ): + max_workers = 10 + executor = ThreadPoolExecutor(max_workers=max_workers) + + expected_span_contexts: List[trace.SpanContext] = [] + futures_list = [] + for num in range(max_workers): + with self._tracer.start_as_current_span(f"trace_{num}") as span: + expected_span_context = span.get_span_context() + expected_span_contexts.append(expected_span_context) + future = executor.submit( + self.get_current_span_context_for_test + ) + futures_list.append(future) + + result_span_contexts = [future.result() for future in futures_list] + + # check result + self.assertEqual(result_span_contexts, expected_span_contexts) + + def test_trace_context_propagation_in_thread_pool_with_single_worker(self): + max_workers = 1 + with ThreadPoolExecutor(max_workers=max_workers) as executor: + # test propagation of the same trace context across multiple tasks + with self._tracer.start_as_current_span("task") as task_span: + expected_task_context = task_span.get_span_context() + future1 = executor.submit( + self.get_current_span_context_for_test + ) + future2 = executor.submit( + self.get_current_span_context_for_test + ) + + # check result + self.assertEqual(future1.result(), expected_task_context) + self.assertEqual(future2.result(), expected_task_context) + + # test propagation of different trace contexts across tasks in sequence + with self._tracer.start_as_current_span("task1") as task1_span: + expected_task1_context = task1_span.get_span_context() + future1 = executor.submit( + self.get_current_span_context_for_test + ) + + # check result + self.assertEqual(future1.result(), expected_task1_context) + + with self._tracer.start_as_current_span("task2") as task2_span: + expected_task2_context = task2_span.get_span_context() + future2 = executor.submit( + self.get_current_span_context_for_test + ) + + # check result + self.assertEqual(future2.result(), expected_task2_context) + + def fake_func(self): + span_context = self.get_current_span_context_for_test() + self._mock_span_contexts.append(span_context) + + @staticmethod + def get_current_span_context_for_test() -> trace.SpanContext: + return trace.get_current_span().get_span_context() + + def print_square(self, num): + with self._tracer.start_as_current_span("square"): + return num * num + + def print_cube(self, num): + with self._tracer.start_as_current_span("cube"): + 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,)) + + 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,) + ) + 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,)) + + with self._tracer.start_as_current_span("root"): + square_thread.start() + square_thread.join() + + 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,) + ) + + with self._tracer.start_as_current_span("root"): + square_thread.start() + square_thread.join() + + 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 + # + calculate_thread = threading.Thread(target=self.calculate, args=(10,)) + + with self._tracer.start_as_current_span("root"): + calculate_thread.start() + 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()) + self.assertIsNone(root.parent) + + def test_uninstrumented(self): + ThreadingInstrumentor().uninstrument() + + 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) + + ThreadingInstrumentor().instrument() diff --git a/opentelemetry-contrib-instrumentations/pyproject.toml b/opentelemetry-contrib-instrumentations/pyproject.toml index 60d1d6740e..7e4afad6f7 100644 --- a/opentelemetry-contrib-instrumentations/pyproject.toml +++ b/opentelemetry-contrib-instrumentations/pyproject.toml @@ -70,6 +70,7 @@ dependencies = [ "opentelemetry-instrumentation-sqlite3==0.46b0.dev", "opentelemetry-instrumentation-starlette==0.46b0.dev", "opentelemetry-instrumentation-system-metrics==0.46b0.dev", + "opentelemetry-instrumentation-threading==0.46b0.dev", "opentelemetry-instrumentation-tornado==0.46b0.dev", "opentelemetry-instrumentation-tortoiseorm==0.46b0.dev", "opentelemetry-instrumentation-urllib==0.46b0.dev", diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py index 30a41b924a..455d08e41a 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py @@ -191,6 +191,7 @@ "opentelemetry-instrumentation-dbapi==0.46b0.dev", "opentelemetry-instrumentation-logging==0.46b0.dev", "opentelemetry-instrumentation-sqlite3==0.46b0.dev", + "opentelemetry-instrumentation-threading==0.46b0.dev", "opentelemetry-instrumentation-urllib==0.46b0.dev", "opentelemetry-instrumentation-wsgi==0.46b0.dev", ] diff --git a/tox.ini b/tox.ini index 068b5d583e..420ae899f0 100644 --- a/tox.ini +++ b/tox.ini @@ -233,6 +233,10 @@ envlist = py3{6,8,9,10,11}-test-instrumentation-system-metrics pypy3-test-instrumentation-system-metrics + ; opentelemetry-instrumentation-threading + py3{8,9,10,11}-test-instrumentation-threading + pypy3-test-instrumentation-threading + ; opentelemetry-instrumentation-tornado py3{8,9,10,11}-test-instrumentation-tornado pypy3-test-instrumentation-tornado @@ -421,6 +425,8 @@ commands_pre = system-metrics: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-system-metrics/test-requirements.txt + threading: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-threading/test-requirements.txt + tornado: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-tornado/test-requirements.txt tortoiseorm: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-tortoiseorm/test-requirements.txt @@ -517,6 +523,7 @@ commands = test-instrumentation-sqlite3: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlite3/tests {posargs} test-instrumentation-starlette: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-starlette/tests {posargs} test-instrumentation-system-metrics: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-system-metrics/tests {posargs} + test-instrumentation-threading: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-threading/tests {posargs} test-instrumentation-tornado: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-tornado/tests {posargs} test-instrumentation-tortoiseorm: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-tortoiseorm/tests {posargs} test-instrumentation-wsgi: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi/tests {posargs} @@ -620,6 +627,7 @@ commands_pre = pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncio/test-requirements.txt pip install -r {toxinidir}/exporter/opentelemetry-exporter-richconsole/test-requirements.txt pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-system-metrics/test-requirements.txt + pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-threading/test-requirements.txt pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-tortoiseorm/test-requirements.txt # requires snappy headers to be available on the system pip install -r {toxinidir}/resource/opentelemetry-resource-detector-container/test-requirements.txt From e84bfff6cf8e57169aa18560a90e4d1d52c3f8d4 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Mon, 15 Apr 2024 18:26:57 +0200 Subject: [PATCH 04/58] readthedocs: update build os config (#2389) --- .readthedocs.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 3dcf0e5cf6..2a3c920b45 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -6,9 +6,10 @@ sphinx: configuration: docs/conf.py build: - image: latest + os: "ubuntu-22.04" + tools: + python: "3.8" python: - version: 3.8 install: - requirements: docs-requirements.txt From 8322ee790e25b9103f4b527244959cad20d7817c Mon Sep 17 00:00:00 2001 From: John Bley Date: Tue, 16 Apr 2024 12:36:37 -0400 Subject: [PATCH 05/58] Fix all issues from shellcheck --severity=warning (#2354) --- .github/workflows/shellcheck.yml | 19 +++++++++++++++++++ .../proto/generate-proto-py.sh | 1 - scripts/build.sh | 4 ++-- scripts/build_a_package.sh | 8 ++++---- scripts/coverage.sh | 3 --- 5 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/shellcheck.yml diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml new file mode 100644 index 0000000000..68d12b805f --- /dev/null +++ b/.github/workflows/shellcheck.yml @@ -0,0 +1,19 @@ +name: Shellcheck + +on: + push: + branches-ignore: + - 'release/*' + pull_request: + +jobs: + shellcheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install shellcheck + run: sudo apt update && sudo apt install --assume-yes shellcheck + + - name: Run shellcheck + run: find . -name \*.sh | xargs shellcheck --severity=warning diff --git a/exporter/opentelemetry-exporter-prometheus-remote-write/proto/generate-proto-py.sh b/exporter/opentelemetry-exporter-prometheus-remote-write/proto/generate-proto-py.sh index 5f6129df55..a07181add3 100755 --- a/exporter/opentelemetry-exporter-prometheus-remote-write/proto/generate-proto-py.sh +++ b/exporter/opentelemetry-exporter-prometheus-remote-write/proto/generate-proto-py.sh @@ -6,7 +6,6 @@ PROTO_VERSION=v1.3.2 # SRC_DIR is from protoc perspective. ie its the destination for our checkouts/clones SRC_DIR=opentelemetry/exporter/prometheus_remote_write/gen/ -DST_DIR=../src/opentelemetry/exporter/prometheus_remote_write/gen/ #TODO: # Check that black & protoc are installed properly diff --git a/scripts/build.sh b/scripts/build.sh index fa490a6a35..247bb24b4e 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -8,13 +8,13 @@ set -ev # Get the latest versions of packaging tools python3 -m pip install --upgrade pip build setuptools wheel -BASEDIR=$(dirname $(readlink -f $(dirname $0))) +BASEDIR=$(dirname "$(readlink -f "$(dirname $0)")") DISTDIR=dist ( cd $BASEDIR mkdir -p $DISTDIR - rm -rf $DISTDIR/* + rm -rf ${DISTDIR:?}/* for d in exporter/*/ opentelemetry-instrumentation/ opentelemetry-contrib-instrumentations/ opentelemetry-distro/ instrumentation/*/ propagator/*/ resource/*/ sdk-extension/*/ util/*/ ; do ( diff --git a/scripts/build_a_package.sh b/scripts/build_a_package.sh index f3baa8a65a..333deccd80 100755 --- a/scripts/build_a_package.sh +++ b/scripts/build_a_package.sh @@ -22,7 +22,7 @@ set -ev if [ -z $GITHUB_REF ]; then echo 'Failed to run script, missing workflow env variable GITHUB_REF.' - exit -1 + exit 1 fi pkg_name_and_version=${GITHUB_REF#refs/tags/*} @@ -40,13 +40,13 @@ cd $basedir distdir=${basedir}/dist mkdir -p $distdir -rm -rf $distdir/* +rm -rf ${distdir:?}/* pyproject_toml_file_path=$(ls **/$pkg_name/pyproject.toml) if [ -z $pyproject_toml_file_path ]; then echo "Error! pyproject.toml not found for $pkg_name, can't build." - exit -1 + exit 1 fi directory_with_package=$(dirname $pyproject_toml_file_path) @@ -61,7 +61,7 @@ pkg_tar_gz_file=${pkg_name}-${pkg_version}.tar.gz if ! [ -f $pkg_tar_gz_file ]; then echo 'Error! Tag version does not match version built using latest package files.' - exit -1 + exit 1 fi # Build a wheel for the source distribution diff --git a/scripts/coverage.sh b/scripts/coverage.sh index 4015c6884a..02ce1eed55 100755 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -11,9 +11,6 @@ function cov { ${1} } -PYTHON_VERSION=$(python -c 'import sys; print(".".join(map(str, sys.version_info[:3])))') -PYTHON_VERSION_INFO=(${PYTHON_VERSION//./ }) - coverage erase cov instrumentation/opentelemetry-instrumentation-flask From 99678ccd3a6ab5afe2661d00a27a6cdf3391c145 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Tue, 16 Apr 2024 14:04:17 -0700 Subject: [PATCH 06/58] Add section for semantic convention status for instrumentations (#2433) --- README.md | 6 +- instrumentation/README.md | 100 +++++++++--------- .../instrumentation/requests/package.py | 2 + scripts/generate_instrumentation_readme.py | 10 +- 4 files changed, 64 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 6968de717e..133acf81f6 100644 --- a/README.md +++ b/README.md @@ -96,11 +96,15 @@ To release a package as `1.0` stable, the package: - e.g. If an instrumentation package uses flags, a token as context, or parameters that are not typical of the `BaseInstrumentor` class, these are documented - After the release of `1.0`, a CODEOWNER may no longer feel like they have the bandwidth to meet the responsibilities of maintaining the package. That's not a problem at all, life happens! However, if that is the case, we ask that the CODEOWNER please raise an issue indicating that they would like to be removed as a CODEOWNER so that they don't get pinged on future PRs. Ultimately, we hope to use that issue to find a new CODEOWNER. +## Semantic Convention status of instrumentations + +In our efforts to maintain optimal user experience and prevent breaking changes for transitioning into stable semantic conventions, OpenTelemetry Python is adopting the [semantic convention migration plan](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/migration-guide.md) for several instrumentations. Currently this plan is only being adopted for HTTP-related instrumentations, but will eventually cover all types. Please refer to the `semconv status` column of the [instrumentation README](instrumentation/README.md) of the current status of instrumentations' semantic conventions. The possible values are `experimental`, `stable` and `migration` referring to [status](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.31.0/specification/document-status.md#lifecycle-status) of that particular semantic convention. `Migration` refers to an instrumentation that currently supports the migration plan. + ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) -We meet weekly on Thursday, and the time of the meeting alternates between 9AM PT and 4PM PT. The meeting is subject to change depending on contributors' availability. Check the [OpenTelemetry community calendar](https://calendar.google.com/calendar/embed?src=google.com_b79e3e90j7bbsa2n2p5an5lf60%40group.calendar.google.com) for specific dates and for the Zoom link. +We meet weekly on Thursday at 9AM PT. The meeting is subject to change depending on contributors' availability. Check the [OpenTelemetry community calendar](https://calendar.google.com/calendar/embed?src=c_2bf73e3b6b530da4babd444e72b76a6ad893a5c3f43cf40467abc7a9a897f977%40group.calendar.google.com) for specific dates and for the Zoom link. Meeting notes are available as a public [Google doc](https://docs.google.com/document/d/1CIMGoIOZ-c3-igzbd6_Pnxx1SjAkjwqoYSUWxPY8XIs/edit). For edit access, get in touch on [GitHub Discussions](https://github.com/open-telemetry/opentelemetry-python/discussions). diff --git a/instrumentation/README.md b/instrumentation/README.md index 62caabd81a..284037d707 100644 --- a/instrumentation/README.md +++ b/instrumentation/README.md @@ -1,51 +1,51 @@ -| Instrumentation | Supported Packages | Metrics support | -| --------------- | ------------------ | --------------- | -| [opentelemetry-instrumentation-aio-pika](./opentelemetry-instrumentation-aio-pika) | aio_pika >= 7.2.0, < 10.0.0 | No -| [opentelemetry-instrumentation-aiohttp-client](./opentelemetry-instrumentation-aiohttp-client) | aiohttp ~= 3.0 | No -| [opentelemetry-instrumentation-aiohttp-server](./opentelemetry-instrumentation-aiohttp-server) | aiohttp ~= 3.0 | No -| [opentelemetry-instrumentation-aiopg](./opentelemetry-instrumentation-aiopg) | aiopg >= 0.13.0, < 2.0.0 | No -| [opentelemetry-instrumentation-asgi](./opentelemetry-instrumentation-asgi) | asgiref ~= 3.0 | No -| [opentelemetry-instrumentation-asyncio](./opentelemetry-instrumentation-asyncio) | asyncio | No -| [opentelemetry-instrumentation-asyncpg](./opentelemetry-instrumentation-asyncpg) | asyncpg >= 0.12.0 | No -| [opentelemetry-instrumentation-aws-lambda](./opentelemetry-instrumentation-aws-lambda) | aws_lambda | No -| [opentelemetry-instrumentation-boto](./opentelemetry-instrumentation-boto) | boto~=2.0 | No -| [opentelemetry-instrumentation-boto3sqs](./opentelemetry-instrumentation-boto3sqs) | boto3 ~= 1.0 | No -| [opentelemetry-instrumentation-botocore](./opentelemetry-instrumentation-botocore) | botocore ~= 1.0 | No -| [opentelemetry-instrumentation-cassandra](./opentelemetry-instrumentation-cassandra) | cassandra-driver ~= 3.25,scylla-driver ~= 3.25 | No -| [opentelemetry-instrumentation-celery](./opentelemetry-instrumentation-celery) | celery >= 4.0, < 6.0 | No -| [opentelemetry-instrumentation-confluent-kafka](./opentelemetry-instrumentation-confluent-kafka) | confluent-kafka >= 1.8.2, <= 2.3.0 | No -| [opentelemetry-instrumentation-dbapi](./opentelemetry-instrumentation-dbapi) | dbapi | No -| [opentelemetry-instrumentation-django](./opentelemetry-instrumentation-django) | django >= 1.10 | Yes -| [opentelemetry-instrumentation-elasticsearch](./opentelemetry-instrumentation-elasticsearch) | elasticsearch >= 2.0 | No -| [opentelemetry-instrumentation-falcon](./opentelemetry-instrumentation-falcon) | falcon >= 1.4.1, < 4.0.0 | Yes -| [opentelemetry-instrumentation-fastapi](./opentelemetry-instrumentation-fastapi) | fastapi ~= 0.58 | Yes -| [opentelemetry-instrumentation-flask](./opentelemetry-instrumentation-flask) | flask >= 1.0 | Yes -| [opentelemetry-instrumentation-grpc](./opentelemetry-instrumentation-grpc) | grpcio ~= 1.27 | No -| [opentelemetry-instrumentation-httpx](./opentelemetry-instrumentation-httpx) | httpx >= 0.18.0 | No -| [opentelemetry-instrumentation-jinja2](./opentelemetry-instrumentation-jinja2) | jinja2 >= 2.7, < 4.0 | No -| [opentelemetry-instrumentation-kafka-python](./opentelemetry-instrumentation-kafka-python) | kafka-python >= 2.0 | No -| [opentelemetry-instrumentation-logging](./opentelemetry-instrumentation-logging) | logging | No -| [opentelemetry-instrumentation-mysql](./opentelemetry-instrumentation-mysql) | mysql-connector-python ~= 8.0 | No -| [opentelemetry-instrumentation-mysqlclient](./opentelemetry-instrumentation-mysqlclient) | mysqlclient < 3 | No -| [opentelemetry-instrumentation-pika](./opentelemetry-instrumentation-pika) | pika >= 0.12.0 | No -| [opentelemetry-instrumentation-psycopg](./opentelemetry-instrumentation-psycopg) | psycopg >= 3.1.0 | No -| [opentelemetry-instrumentation-psycopg2](./opentelemetry-instrumentation-psycopg2) | psycopg2 >= 2.7.3.1 | No -| [opentelemetry-instrumentation-pymemcache](./opentelemetry-instrumentation-pymemcache) | pymemcache >= 1.3.5, < 5 | No -| [opentelemetry-instrumentation-pymongo](./opentelemetry-instrumentation-pymongo) | pymongo >= 3.1, < 5.0 | No -| [opentelemetry-instrumentation-pymysql](./opentelemetry-instrumentation-pymysql) | PyMySQL < 2 | No -| [opentelemetry-instrumentation-pyramid](./opentelemetry-instrumentation-pyramid) | pyramid >= 1.7 | Yes -| [opentelemetry-instrumentation-redis](./opentelemetry-instrumentation-redis) | redis >= 2.6 | No -| [opentelemetry-instrumentation-remoulade](./opentelemetry-instrumentation-remoulade) | remoulade >= 0.50 | No -| [opentelemetry-instrumentation-requests](./opentelemetry-instrumentation-requests) | requests ~= 2.0 | Yes -| [opentelemetry-instrumentation-sklearn](./opentelemetry-instrumentation-sklearn) | scikit-learn ~= 0.24.0 | No -| [opentelemetry-instrumentation-sqlalchemy](./opentelemetry-instrumentation-sqlalchemy) | sqlalchemy | Yes -| [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 -| [opentelemetry-instrumentation-urllib3](./opentelemetry-instrumentation-urllib3) | urllib3 >= 1.0.0, < 3.0.0 | Yes -| [opentelemetry-instrumentation-wsgi](./opentelemetry-instrumentation-wsgi) | wsgi | Yes \ No newline at end of file +| Instrumentation | Supported Packages | Metrics support | Semconv status | +| --------------- | ------------------ | --------------- | -------------- | +| [opentelemetry-instrumentation-aio-pika](./opentelemetry-instrumentation-aio-pika) | aio_pika >= 7.2.0, < 10.0.0 | No | experimental +| [opentelemetry-instrumentation-aiohttp-client](./opentelemetry-instrumentation-aiohttp-client) | aiohttp ~= 3.0 | No | experimental +| [opentelemetry-instrumentation-aiohttp-server](./opentelemetry-instrumentation-aiohttp-server) | aiohttp ~= 3.0 | No | experimental +| [opentelemetry-instrumentation-aiopg](./opentelemetry-instrumentation-aiopg) | aiopg >= 0.13.0, < 2.0.0 | No | experimental +| [opentelemetry-instrumentation-asgi](./opentelemetry-instrumentation-asgi) | asgiref ~= 3.0 | No | experimental +| [opentelemetry-instrumentation-asyncio](./opentelemetry-instrumentation-asyncio) | asyncio | No | experimental +| [opentelemetry-instrumentation-asyncpg](./opentelemetry-instrumentation-asyncpg) | asyncpg >= 0.12.0 | No | experimental +| [opentelemetry-instrumentation-aws-lambda](./opentelemetry-instrumentation-aws-lambda) | aws_lambda | No | experimental +| [opentelemetry-instrumentation-boto](./opentelemetry-instrumentation-boto) | boto~=2.0 | No | experimental +| [opentelemetry-instrumentation-boto3sqs](./opentelemetry-instrumentation-boto3sqs) | boto3 ~= 1.0 | No | experimental +| [opentelemetry-instrumentation-botocore](./opentelemetry-instrumentation-botocore) | botocore ~= 1.0 | No | experimental +| [opentelemetry-instrumentation-cassandra](./opentelemetry-instrumentation-cassandra) | cassandra-driver ~= 3.25,scylla-driver ~= 3.25 | No | experimental +| [opentelemetry-instrumentation-celery](./opentelemetry-instrumentation-celery) | celery >= 4.0, < 6.0 | No | experimental +| [opentelemetry-instrumentation-confluent-kafka](./opentelemetry-instrumentation-confluent-kafka) | confluent-kafka >= 1.8.2, <= 2.3.0 | No | experimental +| [opentelemetry-instrumentation-dbapi](./opentelemetry-instrumentation-dbapi) | dbapi | No | experimental +| [opentelemetry-instrumentation-django](./opentelemetry-instrumentation-django) | django >= 1.10 | Yes | experimental +| [opentelemetry-instrumentation-elasticsearch](./opentelemetry-instrumentation-elasticsearch) | elasticsearch >= 2.0 | No | experimental +| [opentelemetry-instrumentation-falcon](./opentelemetry-instrumentation-falcon) | falcon >= 1.4.1, < 4.0.0 | Yes | experimental +| [opentelemetry-instrumentation-fastapi](./opentelemetry-instrumentation-fastapi) | fastapi ~= 0.58 | Yes | experimental +| [opentelemetry-instrumentation-flask](./opentelemetry-instrumentation-flask) | flask >= 1.0 | Yes | experimental +| [opentelemetry-instrumentation-grpc](./opentelemetry-instrumentation-grpc) | grpcio ~= 1.27 | No | experimental +| [opentelemetry-instrumentation-httpx](./opentelemetry-instrumentation-httpx) | httpx >= 0.18.0 | No | experimental +| [opentelemetry-instrumentation-jinja2](./opentelemetry-instrumentation-jinja2) | jinja2 >= 2.7, < 4.0 | No | experimental +| [opentelemetry-instrumentation-kafka-python](./opentelemetry-instrumentation-kafka-python) | kafka-python >= 2.0 | No | experimental +| [opentelemetry-instrumentation-logging](./opentelemetry-instrumentation-logging) | logging | No | experimental +| [opentelemetry-instrumentation-mysql](./opentelemetry-instrumentation-mysql) | mysql-connector-python ~= 8.0 | No | experimental +| [opentelemetry-instrumentation-mysqlclient](./opentelemetry-instrumentation-mysqlclient) | mysqlclient < 3 | No | experimental +| [opentelemetry-instrumentation-pika](./opentelemetry-instrumentation-pika) | pika >= 0.12.0 | No | experimental +| [opentelemetry-instrumentation-psycopg](./opentelemetry-instrumentation-psycopg) | psycopg >= 3.1.0 | No | experimental +| [opentelemetry-instrumentation-psycopg2](./opentelemetry-instrumentation-psycopg2) | psycopg2 >= 2.7.3.1 | No | experimental +| [opentelemetry-instrumentation-pymemcache](./opentelemetry-instrumentation-pymemcache) | pymemcache >= 1.3.5, < 5 | No | experimental +| [opentelemetry-instrumentation-pymongo](./opentelemetry-instrumentation-pymongo) | pymongo >= 3.1, < 5.0 | No | experimental +| [opentelemetry-instrumentation-pymysql](./opentelemetry-instrumentation-pymysql) | PyMySQL < 2 | No | experimental +| [opentelemetry-instrumentation-pyramid](./opentelemetry-instrumentation-pyramid) | pyramid >= 1.7 | Yes | experimental +| [opentelemetry-instrumentation-redis](./opentelemetry-instrumentation-redis) | redis >= 2.6 | No | experimental +| [opentelemetry-instrumentation-remoulade](./opentelemetry-instrumentation-remoulade) | remoulade >= 0.50 | No | experimental +| [opentelemetry-instrumentation-requests](./opentelemetry-instrumentation-requests) | requests ~= 2.0 | Yes | migration +| [opentelemetry-instrumentation-sklearn](./opentelemetry-instrumentation-sklearn) | scikit-learn ~= 0.24.0 | No | experimental +| [opentelemetry-instrumentation-sqlalchemy](./opentelemetry-instrumentation-sqlalchemy) | sqlalchemy | Yes | experimental +| [opentelemetry-instrumentation-sqlite3](./opentelemetry-instrumentation-sqlite3) | sqlite3 | No | experimental +| [opentelemetry-instrumentation-starlette](./opentelemetry-instrumentation-starlette) | starlette ~= 0.13.0 | Yes | experimental +| [opentelemetry-instrumentation-system-metrics](./opentelemetry-instrumentation-system-metrics) | psutil >= 5 | No | experimental +| [opentelemetry-instrumentation-threading](./opentelemetry-instrumentation-threading) | threading | No | experimental +| [opentelemetry-instrumentation-tornado](./opentelemetry-instrumentation-tornado) | tornado >= 5.1.1 | Yes | experimental +| [opentelemetry-instrumentation-tortoiseorm](./opentelemetry-instrumentation-tortoiseorm) | tortoise-orm >= 0.17.0 | No | experimental +| [opentelemetry-instrumentation-urllib](./opentelemetry-instrumentation-urllib) | urllib | Yes | experimental +| [opentelemetry-instrumentation-urllib3](./opentelemetry-instrumentation-urllib3) | urllib3 >= 1.0.0, < 3.0.0 | Yes | experimental +| [opentelemetry-instrumentation-wsgi](./opentelemetry-instrumentation-wsgi) | wsgi | Yes | experimental \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/package.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/package.py index 8424bfeb2a..9cd93a9150 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/package.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/package.py @@ -16,3 +16,5 @@ _instruments = ("requests ~= 2.0",) _supports_metrics = True + +_semconv_status = "migration" diff --git a/scripts/generate_instrumentation_readme.py b/scripts/generate_instrumentation_readme.py index 00045cd73c..79b8a5533d 100755 --- a/scripts/generate_instrumentation_readme.py +++ b/scripts/generate_instrumentation_readme.py @@ -23,8 +23,8 @@ _prefix = "opentelemetry-instrumentation-" header = """ -| Instrumentation | Supported Packages | Metrics support | -| --------------- | ------------------ | --------------- |""" +| Instrumentation | Supported Packages | Metrics support | Semconv status | +| --------------- | ------------------ | --------------- | -------------- |""" def main(): @@ -63,13 +63,17 @@ def main(): instruments = pkg_info["_instruments"] supports_metrics = pkg_info.get("_supports_metrics") + semconv_status = pkg_info.get("_semconv_status") if not instruments: instruments = (name,) + if not semconv_status: + semconv_status = "experimental" + metric_column = "Yes" if supports_metrics else "No" table.append( - f"| [{instrumentation}](./{instrumentation}) | {','.join(instruments)} | {metric_column}" + f"| [{instrumentation}](./{instrumentation}) | {','.join(instruments)} | {metric_column} | {semconv_status}" ) with open( From 07318bd3782e8db237ab9ead66b4c1e694c90a5e Mon Sep 17 00:00:00 2001 From: Tammy Baylis <96076570+tammy-baylis-swi@users.noreply.github.com> Date: Wed, 17 Apr 2024 09:09:55 -0700 Subject: [PATCH 07/58] Change AwsLambdaInstrumentor span attrs to meet faas semconv (#2372) --- CHANGELOG.md | 2 ++ .../opentelemetry/instrumentation/aws_lambda/__init__.py | 8 ++++---- .../tests/test_aws_lambda_instrumentation_manual.py | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 911ae8fa70..89495abe73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Rename `type` attribute to `asgi.event.type` in `opentelemetry-instrumentation-asgi` ([#2300](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2300)) +- Rename AwsLambdaInstrumentor span attributes `faas.id` to `cloud.resource_id`, `faas.execution` to `faas.invocation_id` + ([#2372](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2372)) ### Added diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py index 3ec22d4c0e..8f29a0ec38 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py @@ -340,17 +340,17 @@ def _instrumented_lambda_handler_call( # noqa pylint: disable=too-many-branches if span.is_recording(): lambda_context = args[1] # NOTE: The specs mention an exception here, allowing the - # `ResourceAttributes.FAAS_ID` attribute to be set as a span + # `SpanAttributes.CLOUD_RESOURCE_ID` attribute to be set as a span # attribute instead of a resource attribute. # # See more: - # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/faas.md#example + # https://github.com/open-telemetry/semantic-conventions/blob/main/docs/faas/aws-lambda.md#resource-detector span.set_attribute( - ResourceAttributes.FAAS_ID, + SpanAttributes.CLOUD_RESOURCE_ID, lambda_context.invoked_function_arn, ) span.set_attribute( - SpanAttributes.FAAS_EXECUTION, + SpanAttributes.FAAS_INVOCATION_ID, lambda_context.aws_request_id, ) diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py index 9bf6f47d7b..51ebb67ebc 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py @@ -145,8 +145,8 @@ def test_active_tracing(self): self.assertSpanHasAttributes( span, { - ResourceAttributes.FAAS_ID: MOCK_LAMBDA_CONTEXT.invoked_function_arn, - SpanAttributes.FAAS_EXECUTION: MOCK_LAMBDA_CONTEXT.aws_request_id, + SpanAttributes.CLOUD_RESOURCE_ID: MOCK_LAMBDA_CONTEXT.invoked_function_arn, + SpanAttributes.FAAS_INVOCATION_ID: MOCK_LAMBDA_CONTEXT.aws_request_id, ResourceAttributes.CLOUD_ACCOUNT_ID: MOCK_LAMBDA_CONTEXT.invoked_function_arn.split( ":" )[ From 82b07959a91bf23a470afcec8710bab956aba2a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Em=C3=ADdio=20Neto?= <9735060+emdneto@users.noreply.github.com> Date: Wed, 17 Apr 2024 13:48:09 -0300 Subject: [PATCH 08/58] fix: instrumentation-celery test tasks CustomError (#2416) * fix: instrumentation-celery test tasks CustomError * chore: lint --------- Co-authored-by: Diego Hurtado --- .../opentelemetry-instrumentation-celery/tests/test_tasks.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-celery/tests/test_tasks.py b/instrumentation/opentelemetry-instrumentation-celery/tests/test_tasks.py index ed4dbb5b1d..3ac6a5a70c 100644 --- a/instrumentation/opentelemetry-instrumentation-celery/tests/test_tasks.py +++ b/instrumentation/opentelemetry-instrumentation-celery/tests/test_tasks.py @@ -125,8 +125,9 @@ def test_task_raises(self): self.assertIn(SpanAttributes.EXCEPTION_STACKTRACE, event.attributes) - self.assertEqual( - event.attributes[SpanAttributes.EXCEPTION_TYPE], "CustomError" + # TODO: use plain assertEqual after 1.25 is released (https://github.com/open-telemetry/opentelemetry-python/pull/3837) + self.assertIn( + "CustomError", event.attributes[SpanAttributes.EXCEPTION_TYPE] ) self.assertEqual( From ab50a4e1514073cb8c299973067c14f4393f899c Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 17 Apr 2024 19:08:18 +0200 Subject: [PATCH 09/58] tox: stop testing on python < 3.8 (#2414) --- tox.ini | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tox.ini b/tox.ini index 420ae899f0..3583b7319f 100644 --- a/tox.ini +++ b/tox.ini @@ -27,7 +27,7 @@ envlist = pypy3-test-instrumentation-aiohttp-client ; opentelemetry-instrumentation-aiohttp-server - py3{6,8,9,10,11}-test-instrumentation-aiohttp-server + py3{8,9,10,11}-test-instrumentation-aiohttp-server pypy3-test-instrumentation-aiohttp-server ; opentelemetry-instrumentation-aiopg @@ -44,7 +44,7 @@ envlist = ; pypy3-test-instrumentation-botocore ; opentelemetry-instrumentation-boto3sqs - py3{6,8,9,10,11}-test-instrumentation-boto3sqs + py3{8,9,10,11}-test-instrumentation-boto3sqs ; FIXME: see https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1736 ; pypy3-test-instrumentation-boto3sqs @@ -144,7 +144,7 @@ envlist = pypy3-test-exporter-richconsole ; opentelemetry-exporter-prometheus-remote-write - py3{6,8,9,10,11}-test-exporter-prometheus-remote-write + py3{8,9,10,11}-test-exporter-prometheus-remote-write pypy3-test-exporter-prometheus-remote-write ; opentelemetry-instrumentation-mysql @@ -160,7 +160,7 @@ envlist = ; ext-psycopg2 intentionally excluded from pypy3 ; opentelemetry-instrumentation-psycopg - py3{7,8,9,10,11}-test-instrumentation-psycopg + py3{8,9,10,11}-test-instrumentation-psycopg pypy3-test-instrumentation-psycopg ; opentelemetry-instrumentation-pymemcache @@ -230,7 +230,7 @@ envlist = py3{8}-test-instrumentation-sklearn ; opentelemetry-instrumentation-system-metrics - py3{6,8,9,10,11}-test-instrumentation-system-metrics + py3{8,9,10,11}-test-instrumentation-system-metrics pypy3-test-instrumentation-system-metrics ; opentelemetry-instrumentation-threading From 9ce1c26d2732dfbdadbb492fc38c562dcd08ed2e Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 17 Apr 2024 20:19:34 +0200 Subject: [PATCH 10/58] django: Bump django version to latest (#2427) --- .../test-requirements-1.txt | 2 +- .../test-requirements-2.txt | 2 +- .../test-requirements-3.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-django/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-django/test-requirements-1.txt index 116dc015ec..548db1b8e3 100644 --- a/instrumentation/opentelemetry-instrumentation-django/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-django/test-requirements-1.txt @@ -1,7 +1,7 @@ asgiref==3.7.2 attrs==23.2.0 Deprecated==1.2.14 -Django==3.2.24 +Django==3.2.25 importlib-metadata==6.11.0 iniconfig==2.0.0 packaging==23.2 diff --git a/instrumentation/opentelemetry-instrumentation-django/test-requirements-2.txt b/instrumentation/opentelemetry-instrumentation-django/test-requirements-2.txt index ac3b40e16b..0970d2f253 100644 --- a/instrumentation/opentelemetry-instrumentation-django/test-requirements-2.txt +++ b/instrumentation/opentelemetry-instrumentation-django/test-requirements-2.txt @@ -2,7 +2,7 @@ asgiref==3.7.2 attrs==23.2.0 backports.zoneinfo==0.2.1 Deprecated==1.2.14 -Django==4.2.10 +Django==4.2.11 importlib-metadata==6.11.0 iniconfig==2.0.0 packaging==23.2 diff --git a/instrumentation/opentelemetry-instrumentation-django/test-requirements-3.txt b/instrumentation/opentelemetry-instrumentation-django/test-requirements-3.txt index 3bb32c4c6b..d853c05877 100644 --- a/instrumentation/opentelemetry-instrumentation-django/test-requirements-3.txt +++ b/instrumentation/opentelemetry-instrumentation-django/test-requirements-3.txt @@ -1,7 +1,7 @@ asgiref==3.7.2 attrs==23.2.0 Deprecated==1.2.14 -Django==4.2.10 +Django==4.2.11 importlib-metadata==6.11.0 iniconfig==2.0.0 packaging==23.2 From 4de0e5659d451baee65af412242b95f174444d87 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Thu, 18 Apr 2024 15:57:26 +0200 Subject: [PATCH 11/58] elasticsearch: stop testing on ancient versions (#2422) Pick 6.0 as the baseline. --- CHANGELOG.md | 2 ++ docs-requirements.txt | 2 +- .../pyproject.toml | 2 +- .../test-requirements-0.txt | 22 ------------- .../test-requirements-1.txt | 22 ------------- ...irements-2.txt => test-requirements-6.txt} | 0 .../tests/helpers_es2.py | 33 ------------------- .../tests/helpers_es5.py | 33 ------------------- .../tests/test_elasticsearch.py | 4 --- .../instrumentation/bootstrap_gen.py | 2 +- tox.ini | 16 +++------ 11 files changed, 10 insertions(+), 128 deletions(-) delete mode 100644 instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-0.txt delete mode 100644 instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt rename instrumentation/opentelemetry-instrumentation-elasticsearch/{test-requirements-2.txt => test-requirements-6.txt} (100%) delete mode 100644 instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es2.py delete mode 100644 instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es5.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 89495abe73..fb92d56507 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2300](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2300)) - Rename AwsLambdaInstrumentor span attributes `faas.id` to `cloud.resource_id`, `faas.execution` to `faas.invocation_id` ([#2372](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2372)) +- Drop support for instrumenting elasticsearch client < 6` + ([#2422](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2422)) ### Added diff --git a/docs-requirements.txt b/docs-requirements.txt index aff449fcf8..72f4472902 100644 --- a/docs-requirements.txt +++ b/docs-requirements.txt @@ -28,7 +28,7 @@ boto3~=1.0 cassandra-driver~=3.25 celery>=4.0 confluent-kafka>= 1.8.2,<= 2.3.0 -elasticsearch>=2.0,<9.0 +elasticsearch>=6.0,<9.0 flask~=2.0 falcon~=2.0 grpcio~=1.27 diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/pyproject.toml b/instrumentation/opentelemetry-instrumentation-elasticsearch/pyproject.toml index 659366181a..f5ba221b7f 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/pyproject.toml @@ -32,7 +32,7 @@ dependencies = [ [project.optional-dependencies] instruments = [ - "elasticsearch >= 2.0", + "elasticsearch >= 6.0", ] [project.entry-points.opentelemetry_instrumentor] diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-0.txt deleted file mode 100644 index 216d1c0b02..0000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-0.txt +++ /dev/null @@ -1,22 +0,0 @@ -asgiref==3.7.2 -attrs==23.2.0 -Deprecated==1.2.14 -elasticsearch==2.4.1 -elasticsearch-dsl==2.2.0 -importlib-metadata==6.11.0 -iniconfig==2.0.0 -packaging==23.2 -pluggy==1.4.0 -py==1.11.0 -py-cpuinfo==9.0.0 -pytest==7.1.3 -pytest-benchmark==4.0.0 -python-dateutil==2.8.2 -six==1.16.0 -tomli==2.0.1 -typing_extensions==4.10.0 -urllib3==1.26.18 -wrapt==1.16.0 -zipp==3.17.0 --e opentelemetry-instrumentation --e instrumentation/opentelemetry-instrumentation-elasticsearch diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt deleted file mode 100644 index 2c51c87508..0000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt +++ /dev/null @@ -1,22 +0,0 @@ -asgiref==3.7.2 -attrs==23.2.0 -Deprecated==1.2.14 -elasticsearch==5.5.3 -elasticsearch-dsl==5.4.0 -importlib-metadata==6.11.0 -iniconfig==2.0.0 -packaging==23.2 -pluggy==1.4.0 -py==1.11.0 -py-cpuinfo==9.0.0 -pytest==7.1.3 -pytest-benchmark==4.0.0 -python-dateutil==2.8.2 -six==1.16.0 -tomli==2.0.1 -typing_extensions==4.10.0 -urllib3==2.2.1 -wrapt==1.16.0 -zipp==3.17.0 --e opentelemetry-instrumentation --e instrumentation/opentelemetry-instrumentation-elasticsearch diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-6.txt similarity index 100% rename from instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt rename to instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-6.txt diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es2.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es2.py deleted file mode 100644 index 008a95d671..0000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es2.py +++ /dev/null @@ -1,33 +0,0 @@ -from elasticsearch_dsl import ( # pylint: disable=no-name-in-module - DocType, - String, -) - - -class Article(DocType): - title = String(analyzer="snowball", fields={"raw": String()}) - body = String(analyzer="snowball") - - class Meta: - index = "test-index" - - -dsl_create_statement = { - "mappings": { - "article": { - "properties": { - "title": { - "analyzer": "snowball", - "fields": {"raw": {"type": "string"}}, - "type": "string", - }, - "body": {"analyzer": "snowball", "type": "string"}, - } - } - }, - "settings": {"analysis": {}}, -} -dsl_index_result = (1, {}, '{"created": true}') -dsl_index_span_name = "Elasticsearch/test-index/article/2" -dsl_index_url = "/test-index/article/2" -dsl_search_method = "GET" diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es5.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es5.py deleted file mode 100644 index cf32d98863..0000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es5.py +++ /dev/null @@ -1,33 +0,0 @@ -from elasticsearch_dsl import ( # pylint: disable=no-name-in-module - DocType, - Keyword, - Text, -) - - -class Article(DocType): - title = Text(analyzer="snowball", fields={"raw": Keyword()}) - body = Text(analyzer="snowball") - - class Meta: - index = "test-index" - - -dsl_create_statement = { - "mappings": { - "article": { - "properties": { - "title": { - "analyzer": "snowball", - "fields": {"raw": {"type": "keyword"}}, - "type": "text", - }, - "body": {"analyzer": "snowball", "type": "text"}, - } - } - }, -} -dsl_index_result = (1, {}, '{"created": true}') -dsl_index_span_name = "Elasticsearch/test-index/article/2" -dsl_index_url = "/test-index/article/2" -dsl_search_method = "GET" diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py index 6008108d79..5d1c85f77d 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py @@ -44,10 +44,6 @@ from . import helpers_es7 as helpers # pylint: disable=no-name-in-module elif major_version == 6: from . import helpers_es6 as helpers # pylint: disable=no-name-in-module -elif major_version == 5: - from . import helpers_es5 as helpers # pylint: disable=no-name-in-module -else: - from . import helpers_es2 as helpers # pylint: disable=no-name-in-module Article = helpers.Article diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py index 455d08e41a..9eebd5bb38 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py @@ -73,7 +73,7 @@ "instrumentation": "opentelemetry-instrumentation-django==0.46b0.dev", }, { - "library": "elasticsearch >= 2.0", + "library": "elasticsearch >= 6.0", "instrumentation": "opentelemetry-instrumentation-elasticsearch==0.46b0.dev", }, { diff --git a/tox.ini b/tox.ini index 3583b7319f..bee544cb70 100644 --- a/tox.ini +++ b/tox.ini @@ -78,13 +78,9 @@ envlist = ; FIXME: Elasticsearch >=7 causes CI workflow tests to hang, see open-telemetry/opentelemetry-python-contrib#620 ; The numbers at the end of the environment names ; below mean these dependencies are being used: - ; 0: elasticsearch-dsl>=2.0,<3.0 elasticsearch>=2.0,<3.0 - ; 1: elasticsearch-dsl>=5.0,<6.0 elasticsearch>=5.0,<6.0 - ; 2: elasticsearch-dsl>=6.0,<7.0 elasticsearch>=6.0,<7.0 - py3{8,9,10,11}-test-instrumentation-elasticsearch-{0,2} - pypy3-test-instrumentation-elasticsearch-{0,2} - py3{8,9}-test-instrumentation-elasticsearch-1 - pypy3-test-instrumentation-elasticsearch-1 + ; 6: elasticsearch-dsl>=6.0,<7.0 elasticsearch>=6.0,<7.0 + py3{8,9,10,11}-test-instrumentation-elasticsearch-6 + pypy3-test-instrumentation-elasticsearch-6 ; opentelemetry-instrumentation-falcon ; py310 does not work with falcon 1 @@ -452,9 +448,7 @@ commands_pre = sqlalchemy-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-0.txt sqlalchemy-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-1.txt - elasticsearch-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-0.txt - elasticsearch-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt - elasticsearch-2: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt + elasticsearch-6: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-6.txt asyncio: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncio/test-requirements.txt @@ -617,7 +611,7 @@ commands_pre = # prerequisite: follow the instructions here https://github.com/PyMySQL/mysqlclient#install # for your OS to install the required dependencies pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-mysqlclient/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt + pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-6.txt pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-tornado/test-requirements.txt pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements.txt pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymongo/test-requirements.txt From 7656bdb8fe46a159158f98cc6de2b6ef07ad793a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9ni?= Date: Thu, 18 Apr 2024 20:33:42 +0200 Subject: [PATCH 12/58] fix(grpc): aio grpc client interceptor incorrect metadata handling (#2363) --- CHANGELOG.md | 5 +++++ .../instrumentation/grpc/_aio_client.py | 12 +++++------- .../tests/_aio_client.py | 8 +++++--- .../tests/_client.py | 16 +++++++++++----- .../tests/test_aio_client_interceptor.py | 9 ++++----- 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb92d56507..146b15a5f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-threading` Initial release for threading ([#2253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2253)) +### Fixed + +- `opentelemetry-instrumentation-grpc` AioClientInterceptor should propagate with a Metadata object + ([#2363](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2363)) + ## Version 1.24.0/0.45b0 (2024-03-28) ### Added diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_aio_client.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_aio_client.py index 8fc992be73..9c8cc5cdf3 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_aio_client.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_aio_client.py @@ -14,10 +14,9 @@ import functools import logging -from collections import OrderedDict import grpc -from grpc.aio import ClientCallDetails +from grpc.aio import ClientCallDetails, Metadata from opentelemetry.instrumentation.grpc._client import ( OpenTelemetryClientInterceptor, @@ -55,20 +54,19 @@ def callback(call): class _BaseAioClientInterceptor(OpenTelemetryClientInterceptor): @staticmethod - def propagate_trace_in_details(client_call_details): + def propagate_trace_in_details(client_call_details: ClientCallDetails): metadata = client_call_details.metadata if not metadata: - mutable_metadata = OrderedDict() + mutable_metadata = Metadata() else: - mutable_metadata = OrderedDict(metadata) + mutable_metadata = Metadata(*tuple(metadata)) inject(mutable_metadata, setter=_carrier_setter) - metadata = tuple(mutable_metadata.items()) return ClientCallDetails( client_call_details.method, client_call_details.timeout, - metadata, + mutable_metadata, client_call_details.credentials, client_call_details.wait_for_ready, ) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/_aio_client.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/_aio_client.py index 9658df1587..6c0b8eac21 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/_aio_client.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/_aio_client.py @@ -21,7 +21,7 @@ async def simple_method(stub, error=False): request = Request( client_id=CLIENT_ID, request_data="error" if error else "data" ) - return await stub.SimpleMethod(request) + return await stub.SimpleMethod(request, metadata=(("key", "value"),)) async def client_streaming_method(stub, error=False): @@ -41,7 +41,7 @@ def server_streaming_method(stub, error=False): client_id=CLIENT_ID, request_data="error" if error else "data" ) - return stub.ServerStreamingMethod(request) + return stub.ServerStreamingMethod(request, metadata=(("key", "value"),)) def bidirectional_streaming_method(stub, error=False): @@ -53,4 +53,6 @@ def request_messages(): ) yield request - return stub.BidirectionalStreamingMethod(request_messages()) + return stub.BidirectionalStreamingMethod( + request_messages(), metadata=(("key", "value"),) + ) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/_client.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/_client.py index 69222b37a4..67e7d0a625 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/_client.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/_client.py @@ -21,14 +21,14 @@ def simple_method(stub, error=False): request = Request( client_id=CLIENT_ID, request_data="error" if error else "data" ) - stub.SimpleMethod(request) + stub.SimpleMethod(request, metadata=(("key", "value"),)) def simple_method_future(stub, error=False): request = Request( client_id=CLIENT_ID, request_data="error" if error else "data" ) - return stub.SimpleMethod.future(request) + return stub.SimpleMethod.future(request, metadata=(("key", "value"),)) def client_streaming_method(stub, error=False): @@ -40,14 +40,18 @@ def request_messages(): ) yield request - stub.ClientStreamingMethod(request_messages()) + stub.ClientStreamingMethod( + request_messages(), metadata=(("key", "value"),) + ) def server_streaming_method(stub, error=False): request = Request( client_id=CLIENT_ID, request_data="error" if error else "data" ) - response_iterator = stub.ServerStreamingMethod(request) + response_iterator = stub.ServerStreamingMethod( + request, metadata=(("key", "value"),) + ) list(response_iterator) @@ -59,6 +63,8 @@ def request_messages(): ) yield request - response_iterator = stub.BidirectionalStreamingMethod(request_messages()) + response_iterator = stub.BidirectionalStreamingMethod( + request_messages(), metadata=(("key", "value"),) + ) list(response_iterator) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor.py index 6b1006b8a3..21dbc44066 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor.py @@ -305,11 +305,10 @@ async def test_client_interceptor_trace_context_propagation(self): await simple_method(stub) metadata = recording_interceptor.recorded_details.metadata - assert len(metadata) == 2 - assert metadata[0][0] == "mock-traceid" - assert metadata[0][1] == "0" - assert metadata[1][0] == "mock-spanid" - assert metadata[1][1] == "0" + assert len(metadata) == 3 + assert metadata.get_all("key") == ["value"] + assert metadata.get_all("mock-traceid") == ["0"] + assert metadata.get_all("mock-spanid") == ["0"] finally: set_global_textmap(previous_propagator) From 2317adcc3478fe0d886d22b05e51d71e4daabfbd Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Fri, 19 Apr 2024 10:59:05 -0700 Subject: [PATCH 13/58] HTTP transition for wsgi (#2425) --- CHANGELOG.md | 17 +- .../tests/test_falcon.py | 29 +- .../tests/test_programmatic.py | 28 +- .../tests/test_automatic.py | 13 +- .../tests/test_programmatic.py | 1 + .../instrumentation/requests/__init__.py | 43 ++- .../tests/test_requests_integration.py | 7 +- .../instrumentation/wsgi/__init__.py | 284 +++++++++----- .../tests/test_wsgi_middleware.py | 354 ++++++++++++++++-- .../opentelemetry/instrumentation/_semconv.py | 313 ++++++++++++---- .../src/opentelemetry/util/http/__init__.py | 9 +- 11 files changed, 852 insertions(+), 246 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 146b15a5f0..6db0e04e68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased -- `opentelemetry-sdk-extension-aws` Register AWS resource detectors under the - `opentelemetry_resource_detector` entry point - ([#2382](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2382)) ### Breaking changes @@ -21,6 +18,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `opentelemetry-sdk-extension-aws` Register AWS resource detectors under the + `opentelemetry_resource_detector` entry point + ([#2382](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2382)) +- `opentelemetry-instrumentation-wsgi` Implement new semantic convention opt-in with stable http semantic conventions + ([#2425](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2425)) - `opentelemetry-instrumentation-threading` Initial release for threading ([#2253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2253)) @@ -29,6 +31,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-grpc` AioClientInterceptor should propagate with a Metadata object ([#2363](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2363)) +### Added + +- `opentelemetry-sdk-extension-aws` Register AWS resource detectors under the `opentelemetry_resource_detector` entry point + ([#2382](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2382)) +- `opentelemetry-instrumentation-wsgi` Implement new semantic convention opt-in with stable http semantic conventions + ([#2425](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2425)) +- `opentelemetry-instrumentation-threading` Initial release for threading + ([#2253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2253)) + ## Version 1.24.0/0.45b0 (2024-03-28) ### Added diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 2245dbfd80..bf7f1d4f49 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -16,21 +16,21 @@ from unittest.mock import Mock, patch import pytest -from falcon import __version__ as _falcon_verison +from falcon import __version__ as _falcon_version from falcon import testing from packaging import version as package_version from opentelemetry import trace +from opentelemetry.instrumentation._semconv import ( + _server_active_requests_count_attrs_old, + _server_duration_attrs_old, +) from opentelemetry.instrumentation.falcon import FalconInstrumentor from opentelemetry.instrumentation.propagators import ( TraceResponsePropagator, get_global_response_propagator, set_global_response_propagator, ) -from opentelemetry.instrumentation.wsgi import ( - _active_requests_count_attrs, - _duration_attrs, -) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, NumberDataPoint, @@ -53,8 +53,8 @@ "http.server.duration", ] _recommended_attrs = { - "http.server.active_requests": _active_requests_count_attrs, - "http.server.duration": _duration_attrs, + "http.server.active_requests": _server_active_requests_count_attrs_old, + "http.server.duration": _server_duration_attrs_old, } @@ -125,7 +125,7 @@ def _test_method(self, method): SpanAttributes.NET_HOST_PORT: 80, SpanAttributes.HTTP_HOST: "falconframework.org", SpanAttributes.HTTP_TARGET: "/", - SpanAttributes.NET_PEER_PORT: "65133", + SpanAttributes.NET_PEER_PORT: 65133, SpanAttributes.HTTP_FLAVOR: "1.1", "falcon.resource": "HelloWorldResource", SpanAttributes.HTTP_STATUS_CODE: 201, @@ -156,7 +156,7 @@ def test_404(self): SpanAttributes.NET_HOST_PORT: 80, SpanAttributes.HTTP_HOST: "falconframework.org", SpanAttributes.HTTP_TARGET: "/", - SpanAttributes.NET_PEER_PORT: "65133", + SpanAttributes.NET_PEER_PORT: 65133, SpanAttributes.HTTP_FLAVOR: "1.1", SpanAttributes.HTTP_STATUS_CODE: 404, }, @@ -193,7 +193,7 @@ def test_500(self): SpanAttributes.NET_HOST_PORT: 80, SpanAttributes.HTTP_HOST: "falconframework.org", SpanAttributes.HTTP_TARGET: "/", - SpanAttributes.NET_PEER_PORT: "65133", + SpanAttributes.NET_PEER_PORT: 65133, SpanAttributes.HTTP_FLAVOR: "1.1", SpanAttributes.HTTP_STATUS_CODE: 500, }, @@ -226,7 +226,7 @@ def test_url_template(self): SpanAttributes.NET_HOST_PORT: 80, SpanAttributes.HTTP_HOST: "falconframework.org", SpanAttributes.HTTP_TARGET: "/", - SpanAttributes.NET_PEER_PORT: "65133", + SpanAttributes.NET_PEER_PORT: 65133, SpanAttributes.HTTP_FLAVOR: "1.1", "falcon.resource": "UserResource", SpanAttributes.HTTP_STATUS_CODE: 200, @@ -336,6 +336,7 @@ def test_falcon_metric_values(self): "http.flavor": "1.1", "http.server_name": "falconframework.org", "net.host.port": 80, + "net.host.name": "falconframework.org", "http.status_code": 404, } expected_requests_count_attributes = { @@ -344,6 +345,8 @@ def test_falcon_metric_values(self): "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "falconframework.org", + "net.host.name": "falconframework.org", + "net.host.port": 80, } start = default_timer() self.client().simulate_get("/hello/756") @@ -523,7 +526,7 @@ def test_custom_request_header_not_added_in_internal_span(self): self.assertNotIn(key, span.attributes) @pytest.mark.skipif( - condition=package_version.parse(_falcon_verison) + condition=package_version.parse(_falcon_version) < package_version.parse("2.0.0"), reason="falcon<2 does not implement custom response headers", ) @@ -558,7 +561,7 @@ def test_custom_response_header_added_in_server_span(self): self.assertNotIn(key, span.attributes) @pytest.mark.skipif( - condition=package_version.parse(_falcon_verison) + condition=package_version.parse(_falcon_version) < package_version.parse("2.0.0"), reason="falcon<2 does not implement custom response headers", ) diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py index dec265907f..82ca88460c 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py +++ b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py @@ -18,17 +18,17 @@ from flask import Flask, request from opentelemetry import trace +from opentelemetry.instrumentation._semconv import ( + _server_active_requests_count_attrs_old, + _server_duration_attrs_old, +) from opentelemetry.instrumentation.flask import FlaskInstrumentor from opentelemetry.instrumentation.propagators import ( TraceResponsePropagator, get_global_response_propagator, set_global_response_propagator, ) -from opentelemetry.instrumentation.wsgi import ( - OpenTelemetryMiddleware, - _active_requests_count_attrs, - _duration_attrs, -) +from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, NumberDataPoint, @@ -54,6 +54,7 @@ def expected_attributes(override_attributes): SpanAttributes.HTTP_SERVER_NAME: "localhost", SpanAttributes.HTTP_SCHEME: "http", SpanAttributes.NET_HOST_PORT: 80, + SpanAttributes.NET_HOST_NAME: "localhost", SpanAttributes.HTTP_HOST: "localhost", SpanAttributes.HTTP_TARGET: "/", SpanAttributes.HTTP_FLAVOR: "1.1", @@ -69,8 +70,8 @@ def expected_attributes(override_attributes): "http.server.duration", ] _recommended_attrs = { - "http.server.active_requests": _active_requests_count_attrs, - "http.server.duration": _duration_attrs, + "http.server.active_requests": _server_active_requests_count_attrs_old, + "http.server.duration": _server_duration_attrs_old, } @@ -358,6 +359,7 @@ def test_basic_metric_success(self): "http.server_name": "localhost", "net.host.port": 80, "http.status_code": 200, + "net.host.name": "localhost", } expected_requests_count_attributes = { "http.method": "GET", @@ -365,6 +367,8 @@ def test_basic_metric_success(self): "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "localhost", + "net.host.name": "localhost", + "net.host.port": 80, } self._assert_basic_metric( expected_duration_attributes, @@ -374,20 +378,23 @@ def test_basic_metric_success(self): def test_basic_metric_nonstandard_http_method_success(self): self.client.open("/hello/756", method="NONSTANDARD") expected_duration_attributes = { - "http.method": "UNKNOWN", + "http.method": "_OTHER", "http.host": "localhost", "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "localhost", "net.host.port": 80, "http.status_code": 405, + "net.host.name": "localhost", } expected_requests_count_attributes = { - "http.method": "UNKNOWN", + "http.method": "_OTHER", "http.host": "localhost", "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "localhost", + "net.host.name": "localhost", + "net.host.port": 80, } self._assert_basic_metric( expected_duration_attributes, @@ -410,6 +417,7 @@ def test_basic_metric_nonstandard_http_method_allowed_success(self): "http.server_name": "localhost", "net.host.port": 80, "http.status_code": 405, + "net.host.name": "localhost", } expected_requests_count_attributes = { "http.method": "NONSTANDARD", @@ -417,6 +425,8 @@ def test_basic_metric_nonstandard_http_method_allowed_success(self): "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "localhost", + "net.host.name": "localhost", + "net.host.port": 80, } self._assert_basic_metric( expected_duration_attributes, diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py index 2c3ec85e18..4715e0b461 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py +++ b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py @@ -18,6 +18,10 @@ from pyramid.config import Configurator from opentelemetry import trace +from opentelemetry.instrumentation._semconv import ( + _server_active_requests_count_attrs_old, + _server_duration_attrs_old, +) from opentelemetry.instrumentation.pyramid import PyramidInstrumentor from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, @@ -31,8 +35,6 @@ OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE, - _active_requests_count_attrs, - _duration_attrs, ) # pylint: disable=import-error @@ -43,8 +45,8 @@ "http.server.duration", ] _recommended_attrs = { - "http.server.active_requests": _active_requests_count_attrs, - "http.server.duration": _duration_attrs, + "http.server.active_requests": _server_active_requests_count_attrs_old, + "http.server.duration": _server_duration_attrs_old, } @@ -213,6 +215,7 @@ def test_basic_metric_success(self): "http.server_name": "localhost", "net.host.port": 80, "http.status_code": 200, + "net.host.name": "localhost", } expected_requests_count_attributes = { "http.method": "GET", @@ -220,6 +223,8 @@ def test_basic_metric_success(self): "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "localhost", + "net.host.name": "localhost", + "net.host.port": 80, } metrics_list = self.memory_metrics_reader.get_metrics_data() for metric in ( diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py index 478eab1937..c566c301d8 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py +++ b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py @@ -37,6 +37,7 @@ def expected_attributes(override_attributes): SpanAttributes.HTTP_SERVER_NAME: "localhost", SpanAttributes.HTTP_SCHEME: "http", SpanAttributes.NET_HOST_PORT: 80, + SpanAttributes.NET_HOST_NAME: "localhost", SpanAttributes.HTTP_HOST: "localhost", SpanAttributes.HTTP_TARGET: "/", SpanAttributes.HTTP_FLAVOR: "1.1", diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index d0150d57b7..2052fe47cd 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -63,18 +63,20 @@ _SPAN_ATTRIBUTES_ERROR_TYPE, _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS, _SPAN_ATTRIBUTES_NETWORK_PEER_PORT, - _filter_duration_attrs, + _client_duration_attrs_new, + _client_duration_attrs_old, + _filter_semconv_duration_attrs, _get_schema_url, + _HTTPStabilityMode, _OpenTelemetrySemanticConventionStability, - _OpenTelemetryStabilityMode, _OpenTelemetryStabilitySignalType, _report_new, _report_old, - _set_http_hostname, + _set_http_host, _set_http_method, - _set_http_net_peer_name, + _set_http_net_peer_name_client, _set_http_network_protocol_version, - _set_http_port, + _set_http_peer_port_client, _set_http_scheme, _set_http_status_code, _set_http_url, @@ -117,7 +119,7 @@ def _instrument( request_hook: _RequestHookT = None, response_hook: _ResponseHookT = None, excluded_urls: ExcludeList = None, - sem_conv_opt_in_mode: _OpenTelemetryStabilityMode = _OpenTelemetryStabilityMode.DEFAULT, + sem_conv_opt_in_mode: _HTTPStabilityMode = _HTTPStabilityMode.DEFAULT, ): """Enables tracing of all requests calls that go through :code:`requests.session.Session.request` (this includes @@ -174,14 +176,14 @@ def get_or_create_headers(): metric_labels, parsed_url.scheme, sem_conv_opt_in_mode ) if parsed_url.hostname: - _set_http_hostname( + _set_http_host( metric_labels, parsed_url.hostname, sem_conv_opt_in_mode ) - _set_http_net_peer_name( + _set_http_net_peer_name_client( metric_labels, parsed_url.hostname, sem_conv_opt_in_mode ) if _report_new(sem_conv_opt_in_mode): - _set_http_hostname( + _set_http_host( span_attributes, parsed_url.hostname, sem_conv_opt_in_mode, @@ -191,11 +193,11 @@ def get_or_create_headers(): _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS ] = parsed_url.hostname if parsed_url.port: - _set_http_port( + _set_http_peer_port_client( metric_labels, parsed_url.port, sem_conv_opt_in_mode ) if _report_new(sem_conv_opt_in_mode): - _set_http_port( + _set_http_peer_port_client( span_attributes, parsed_url.port, sem_conv_opt_in_mode ) # Use semconv library when available @@ -284,16 +286,22 @@ def get_or_create_headers(): ).__qualname__ if duration_histogram_old is not None: - duration_attrs_old = _filter_duration_attrs( - metric_labels, _OpenTelemetryStabilityMode.DEFAULT + duration_attrs_old = _filter_semconv_duration_attrs( + metric_labels, + _client_duration_attrs_old, + _client_duration_attrs_new, + _HTTPStabilityMode.DEFAULT, ) duration_histogram_old.record( max(round(elapsed_time * 1000), 0), attributes=duration_attrs_old, ) if duration_histogram_new is not None: - duration_attrs_new = _filter_duration_attrs( - metric_labels, _OpenTelemetryStabilityMode.HTTP + duration_attrs_new = _filter_semconv_duration_attrs( + metric_labels, + _client_duration_attrs_old, + _client_duration_attrs_new, + _HTTPStabilityMode.HTTP, ) duration_histogram_new.record( elapsed_time, attributes=duration_attrs_new @@ -341,7 +349,10 @@ def get_default_span_name(method): Returns: span name """ - return sanitize_method(method.upper().strip()) + method = sanitize_method(method.upper().strip()) + if method == "_OTHER": + return "HTTP" + return method class RequestsInstrumentor(BaseInstrumentor): diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index 8817053068..d85d70e20e 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -23,10 +23,10 @@ import opentelemetry.instrumentation.requests from opentelemetry import trace from opentelemetry.instrumentation._semconv import ( - _OTEL_SEMCONV_STABILITY_OPT_IN_KEY, _SPAN_ATTRIBUTES_ERROR_TYPE, _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS, _SPAN_ATTRIBUTES_NETWORK_PEER_PORT, + OTEL_SEMCONV_STABILITY_OPT_IN, _OpenTelemetrySemanticConventionStability, ) from opentelemetry.instrumentation.requests import RequestsInstrumentor @@ -88,7 +88,7 @@ def setUp(self): "os.environ", { "OTEL_PYTHON_REQUESTS_EXCLUDED_URLS": "http://localhost/env_excluded_arg/123,env_excluded_noarg", - _OTEL_SEMCONV_STABILITY_OPT_IN_KEY: sem_conv_mode, + OTEL_SEMCONV_STABILITY_OPT_IN: sem_conv_mode, }, ) @@ -524,7 +524,6 @@ def test_requests_exception_new_semconv(self, *_, **__): self.perform_request(url_with_port) span = self.assert_span() - print(span.attributes) self.assertEqual( span.attributes, { @@ -671,7 +670,7 @@ def setUp(self): self.env_patch = mock.patch.dict( "os.environ", { - _OTEL_SEMCONV_STABILITY_OPT_IN_KEY: sem_conv_mode, + OTEL_SEMCONV_STABILITY_OPT_IN: sem_conv_mode, }, ) self.env_patch.start() diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 50d4f03dff..0a873d0fc3 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -213,10 +213,34 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he from timeit import default_timer from opentelemetry import context, trace -from opentelemetry.instrumentation.utils import ( - _start_internal_or_server_span, - http_status_to_status_code, +from opentelemetry.instrumentation._semconv import ( + _METRIC_ATTRIBUTES_SERVER_DURATION_NAME, + _SPAN_ATTRIBUTES_ERROR_TYPE, + _filter_semconv_active_request_count_attr, + _filter_semconv_duration_attrs, + _get_schema_url, + _HTTPStabilityMode, + _OpenTelemetrySemanticConventionStability, + _OpenTelemetryStabilitySignalType, + _report_new, + _report_old, + _server_active_requests_count_attrs_new, + _server_active_requests_count_attrs_old, + _server_duration_attrs_new, + _server_duration_attrs_old, + _set_http_flavor_version, + _set_http_method, + _set_http_net_host, + _set_http_net_host_port, + _set_http_net_peer_name_server, + _set_http_peer_ip, + _set_http_peer_port_server, + _set_http_scheme, + _set_http_target, + _set_http_user_agent, + _set_status, ) +from opentelemetry.instrumentation.utils import _start_internal_or_server_span from opentelemetry.instrumentation.wsgi.version import __version__ from opentelemetry.metrics import get_meter from opentelemetry.propagators.textmap import Getter @@ -228,6 +252,7 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE, SanitizeValue, + _parse_url_query, get_custom_headers, normalise_request_header_name, normalise_response_header_name, @@ -239,26 +264,6 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he _CARRIER_KEY_PREFIX = "HTTP_" _CARRIER_KEY_PREFIX_LEN = len(_CARRIER_KEY_PREFIX) -# List of recommended attributes -_duration_attrs = [ - SpanAttributes.HTTP_METHOD, - SpanAttributes.HTTP_HOST, - SpanAttributes.HTTP_SCHEME, - SpanAttributes.HTTP_STATUS_CODE, - SpanAttributes.HTTP_FLAVOR, - SpanAttributes.HTTP_SERVER_NAME, - SpanAttributes.NET_HOST_NAME, - SpanAttributes.NET_HOST_PORT, -] - -_active_requests_count_attrs = [ - SpanAttributes.HTTP_METHOD, - SpanAttributes.HTTP_HOST, - SpanAttributes.HTTP_SCHEME, - SpanAttributes.HTTP_FLAVOR, - SpanAttributes.HTTP_SERVER_NAME, -] - class WSGIGetter(Getter[dict]): def get( @@ -296,53 +301,84 @@ def setifnotnone(dic, key, value): dic[key] = value -def collect_request_attributes(environ): +# pylint: disable=too-many-branches + + +def collect_request_attributes( + environ, + sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, +): """Collects HTTP request attributes from the PEP3333-conforming WSGI environ and returns a dictionary to be used as span creation attributes. """ + result = {} + _set_http_method( + result, + environ.get("REQUEST_METHOD", ""), + sanitize_method(environ.get("REQUEST_METHOD", "")), + sem_conv_opt_in_mode, + ) + # old semconv v1.12.0 + server_name = environ.get("SERVER_NAME") + if _report_old(sem_conv_opt_in_mode): + result[SpanAttributes.HTTP_SERVER_NAME] = server_name + + _set_http_scheme( + result, + environ.get("wsgi.url_scheme"), + sem_conv_opt_in_mode, + ) - result = { - SpanAttributes.HTTP_METHOD: sanitize_method( - environ.get("REQUEST_METHOD") - ), - SpanAttributes.HTTP_SERVER_NAME: environ.get("SERVER_NAME"), - SpanAttributes.HTTP_SCHEME: environ.get("wsgi.url_scheme"), - } - + host = environ.get("HTTP_HOST") host_port = environ.get("SERVER_PORT") - if host_port is not None and not host_port == "": - result.update({SpanAttributes.NET_HOST_PORT: int(host_port)}) + if host: + _set_http_net_host(result, host, sem_conv_opt_in_mode) + # old semconv v1.12.0 + if _report_old(sem_conv_opt_in_mode): + result[SpanAttributes.HTTP_HOST] = host + if host_port: + _set_http_net_host_port( + result, + int(host_port), + sem_conv_opt_in_mode, + ) - setifnotnone(result, SpanAttributes.HTTP_HOST, environ.get("HTTP_HOST")) target = environ.get("RAW_URI") if target is None: # Note: `"" or None is None` target = environ.get("REQUEST_URI") - if target is not None: - result[SpanAttributes.HTTP_TARGET] = target + if target: + path, query = _parse_url_query(target) + _set_http_target(result, target, path, query, sem_conv_opt_in_mode) else: - result[SpanAttributes.HTTP_URL] = remove_url_credentials( - wsgiref_util.request_uri(environ) - ) + # old semconv v1.20.0 + if _report_old(sem_conv_opt_in_mode): + result[SpanAttributes.HTTP_URL] = remove_url_credentials( + wsgiref_util.request_uri(environ) + ) remote_addr = environ.get("REMOTE_ADDR") if remote_addr: - result[SpanAttributes.NET_PEER_IP] = remote_addr + _set_http_peer_ip(result, remote_addr, sem_conv_opt_in_mode) + + peer_port = environ.get("REMOTE_PORT") + if peer_port: + _set_http_peer_port_server(result, peer_port, sem_conv_opt_in_mode) + remote_host = environ.get("REMOTE_HOST") if remote_host and remote_host != remote_addr: - result[SpanAttributes.NET_PEER_NAME] = remote_host + _set_http_net_peer_name_server( + result, remote_host, sem_conv_opt_in_mode + ) user_agent = environ.get("HTTP_USER_AGENT") if user_agent is not None and len(user_agent) > 0: - result[SpanAttributes.HTTP_USER_AGENT] = user_agent + _set_http_user_agent(result, user_agent, sem_conv_opt_in_mode) - setifnotnone( - result, SpanAttributes.NET_PEER_PORT, environ.get("REMOTE_PORT") - ) flavor = environ.get("SERVER_PROTOCOL", "") if flavor.upper().startswith(_HTTP_VERSION_PREFIX): flavor = flavor[len(_HTTP_VERSION_PREFIX) :] if flavor: - result[SpanAttributes.HTTP_FLAVOR] = flavor + _set_http_flavor_version(result, flavor, sem_conv_opt_in_mode) return result @@ -410,46 +446,56 @@ def _parse_status_code(resp_status): return None -def _parse_active_request_count_attrs(req_attrs): - active_requests_count_attrs = {} - for attr_key in _active_requests_count_attrs: - if req_attrs.get(attr_key) is not None: - active_requests_count_attrs[attr_key] = req_attrs[attr_key] - return active_requests_count_attrs +def _parse_active_request_count_attrs( + req_attrs, sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT +): + return _filter_semconv_active_request_count_attr( + req_attrs, + _server_active_requests_count_attrs_old, + _server_active_requests_count_attrs_new, + sem_conv_opt_in_mode, + ) -def _parse_duration_attrs(req_attrs): - duration_attrs = {} - for attr_key in _duration_attrs: - if req_attrs.get(attr_key) is not None: - duration_attrs[attr_key] = req_attrs[attr_key] - return duration_attrs +def _parse_duration_attrs( + req_attrs, sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT +): + return _filter_semconv_duration_attrs( + req_attrs, + _server_duration_attrs_old, + _server_duration_attrs_new, + sem_conv_opt_in_mode, + ) def add_response_attributes( - span, start_response_status, response_headers + span, + start_response_status, + response_headers, + duration_attrs=None, + sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, ): # pylint: disable=unused-argument """Adds HTTP response attributes to span using the arguments passed to a PEP3333-conforming start_response callable. """ if not span.is_recording(): return - status_code, _ = start_response_status.split(" ", 1) + status_code_str, _ = start_response_status.split(" ", 1) + status_code = 0 try: - status_code = int(status_code) + status_code = int(status_code_str) except ValueError: - span.set_status( - Status( - StatusCode.ERROR, - "Non-integer HTTP status: " + repr(status_code), - ) - ) - else: - span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) - span.set_status( - Status(http_status_to_status_code(status_code, server_span=True)) - ) + status_code = -1 + if duration_attrs is None: + duration_attrs = {} + _set_status( + span, + duration_attrs, + status_code_str, + status_code, + sem_conv_opt_in_mode, + ) def get_default_span_name(environ): @@ -464,6 +510,8 @@ def get_default_span_name(environ): The span name. """ method = sanitize_method(environ.get("REQUEST_METHOD", "").strip()) + if method == "_OTHER": + return "HTTP" path = environ.get("PATH_INFO", "").strip() if method and path: return f"{method} {path}" @@ -495,42 +543,66 @@ def __init__( tracer_provider=None, meter_provider=None, ): + # initialize semantic conventions opt-in if needed + _OpenTelemetrySemanticConventionStability._initialize() + sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode( + _OpenTelemetryStabilitySignalType.HTTP, + ) self.wsgi = wsgi self.tracer = trace.get_tracer( __name__, __version__, tracer_provider, - schema_url="https://opentelemetry.io/schemas/1.11.0", + schema_url=_get_schema_url(sem_conv_opt_in_mode), ) self.meter = get_meter( __name__, __version__, meter_provider, - schema_url="https://opentelemetry.io/schemas/1.11.0", - ) - self.duration_histogram = self.meter.create_histogram( - name=MetricInstruments.HTTP_SERVER_DURATION, - unit="ms", - description="Duration of HTTP client requests.", + schema_url=_get_schema_url(sem_conv_opt_in_mode), ) + self.duration_histogram_old = None + if _report_old(sem_conv_opt_in_mode): + self.duration_histogram_old = self.meter.create_histogram( + name=MetricInstruments.HTTP_SERVER_DURATION, + unit="ms", + description="measures the duration of the inbound HTTP request", + ) + self.duration_histogram_new = None + if _report_new(sem_conv_opt_in_mode): + self.duration_histogram_new = self.meter.create_histogram( + name=_METRIC_ATTRIBUTES_SERVER_DURATION_NAME, + unit="s", + description="measures the duration of the inbound HTTP request", + ) + # We don't need a separate active request counter for old/new semantic conventions + # because the new attributes are a subset of the old attributes self.active_requests_counter = self.meter.create_up_down_counter( name=MetricInstruments.HTTP_SERVER_ACTIVE_REQUESTS, - unit="requests", - description="measures the number of concurrent HTTP requests that are currently in-flight", + unit="{request}", + description="Number of active HTTP server requests.", ) self.request_hook = request_hook self.response_hook = response_hook + self._sem_conv_opt_in_mode = sem_conv_opt_in_mode @staticmethod def _create_start_response( - span, start_response, response_hook, duration_attrs + span, + start_response, + response_hook, + duration_attrs, + sem_conv_opt_in_mode, ): @functools.wraps(start_response) def _start_response(status, response_headers, *args, **kwargs): - add_response_attributes(span, status, response_headers) - status_code = _parse_status_code(status) - if status_code is not None: - duration_attrs[SpanAttributes.HTTP_STATUS_CODE] = status_code + add_response_attributes( + span, + status, + response_headers, + duration_attrs, + sem_conv_opt_in_mode, + ) if span.is_recording() and span.kind == trace.SpanKind.SERVER: custom_attributes = collect_custom_response_headers_attributes( response_headers @@ -551,11 +623,13 @@ def __call__(self, environ, start_response): environ: A WSGI environment. start_response: The WSGI start_response callable. """ - req_attrs = collect_request_attributes(environ) + req_attrs = collect_request_attributes( + environ, self._sem_conv_opt_in_mode + ) active_requests_count_attrs = _parse_active_request_count_attrs( - req_attrs + req_attrs, + self._sem_conv_opt_in_mode, ) - duration_attrs = _parse_duration_attrs(req_attrs) span, token = _start_internal_or_server_span( tracer=self.tracer, @@ -584,20 +658,42 @@ def __call__(self, environ, start_response): try: with trace.use_span(span): start_response = self._create_start_response( - span, start_response, response_hook, duration_attrs + span, + start_response, + response_hook, + req_attrs, + self._sem_conv_opt_in_mode, ) iterable = self.wsgi(environ, start_response) return _end_span_after_iterating(iterable, span, token) except Exception as ex: - if span.is_recording(): + if _report_new(self._sem_conv_opt_in_mode): + req_attrs[_SPAN_ATTRIBUTES_ERROR_TYPE] = type(ex).__qualname__ + if span.is_recording(): + span.set_attribute( + _SPAN_ATTRIBUTES_ERROR_TYPE, type(ex).__qualname__ + ) span.set_status(Status(StatusCode.ERROR, str(ex))) span.end() if token is not None: context.detach(token) raise finally: - duration = max(round((default_timer() - start) * 1000), 0) - self.duration_histogram.record(duration, duration_attrs) + duration_s = default_timer() - start + if self.duration_histogram_old: + duration_attrs_old = _parse_duration_attrs( + req_attrs, _HTTPStabilityMode.DEFAULT + ) + self.duration_histogram_old.record( + max(round(duration_s * 1000), 0), duration_attrs_old + ) + if self.duration_histogram_new: + duration_attrs_new = _parse_duration_attrs( + req_attrs, _HTTPStabilityMode.HTTP + ) + self.duration_histogram_new.record( + max(duration_s, 0), duration_attrs_new + ) self.active_requests_counter.add(-1, active_requests_count_attrs) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index f74dd67867..985fbe0571 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# pylint: disable=too-many-lines + import sys import unittest import wsgiref.util as wsgiref_util @@ -20,6 +22,15 @@ import opentelemetry.instrumentation.wsgi as otel_wsgi from opentelemetry import trace as trace_api +from opentelemetry.instrumentation._semconv import ( + OTEL_SEMCONV_STABILITY_OPT_IN, + _HTTPStabilityMode, + _OpenTelemetrySemanticConventionStability, + _server_active_requests_count_attrs_new, + _server_active_requests_count_attrs_old, + _server_duration_attrs_new, + _server_duration_attrs_old, +) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, NumberDataPoint, @@ -127,17 +138,58 @@ def wsgi_with_repeat_custom_response_headers(environ, start_response): return [b"*"] -_expected_metric_names = [ +_expected_metric_names_old = [ "http.server.active_requests", "http.server.duration", ] -_recommended_attrs = { - "http.server.active_requests": otel_wsgi._active_requests_count_attrs, - "http.server.duration": otel_wsgi._duration_attrs, +_expected_metric_names_new = [ + "http.server.active_requests", + "http.server.request.duration", +] +_recommended_metrics_attrs_old = { + "http.server.active_requests": _server_active_requests_count_attrs_old, + "http.server.duration": _server_duration_attrs_old, +} +_recommended_metrics_attrs_new = { + "http.server.active_requests": _server_active_requests_count_attrs_new, + "http.server.request.duration": _server_duration_attrs_new, +} +_server_active_requests_count_attrs_both = ( + _server_active_requests_count_attrs_old +) +_server_active_requests_count_attrs_both.extend( + _server_active_requests_count_attrs_new +) +_recommended_metrics_attrs_both = { + "http.server.active_requests": _server_active_requests_count_attrs_both, + "http.server.duration": _server_duration_attrs_old, + "http.server.request.duration": _server_duration_attrs_new, } class TestWsgiApplication(WsgiTestBase): + def setUp(self): + super().setUp() + + test_name = "" + if hasattr(self, "_testMethodName"): + test_name = self._testMethodName + sem_conv_mode = "default" + if "new_semconv" in test_name: + sem_conv_mode = "http" + elif "both_semconv" in test_name: + sem_conv_mode = "http/dup" + self.env_patch = mock.patch.dict( + "os.environ", + { + OTEL_SEMCONV_STABILITY_OPT_IN: sem_conv_mode, + }, + ) + + _OpenTelemetrySemanticConventionStability._initialized = False + + self.env_patch.start() + def validate_response( self, response, @@ -146,6 +198,8 @@ def validate_response( http_method="GET", span_attributes=None, response_headers=None, + old_sem_conv=True, + new_sem_conv=False, ): while True: try: @@ -171,7 +225,8 @@ def validate_response( self.assertEqual(len(span_list), 1) self.assertEqual(span_list[0].name, span_name) self.assertEqual(span_list[0].kind, trace_api.SpanKind.SERVER) - expected_attributes = { + expected_attributes = {} + expected_attributes_old = { SpanAttributes.HTTP_SERVER_NAME: "127.0.0.1", SpanAttributes.HTTP_SCHEME: "http", SpanAttributes.NET_HOST_PORT: 80, @@ -179,10 +234,27 @@ def validate_response( SpanAttributes.HTTP_FLAVOR: "1.0", SpanAttributes.HTTP_URL: "http://127.0.0.1/", SpanAttributes.HTTP_STATUS_CODE: 200, + SpanAttributes.NET_HOST_NAME: "127.0.0.1", } + expected_attributes_new = { + SpanAttributes.SERVER_PORT: 80, + SpanAttributes.SERVER_ADDRESS: "127.0.0.1", + SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.0", + SpanAttributes.HTTP_RESPONSE_STATUS_CODE: 200, + } + if old_sem_conv: + expected_attributes.update(expected_attributes_old) + if new_sem_conv: + expected_attributes.update(expected_attributes_new) + expected_attributes.update(span_attributes or {}) if http_method is not None: - expected_attributes[SpanAttributes.HTTP_METHOD] = http_method + if old_sem_conv: + expected_attributes[SpanAttributes.HTTP_METHOD] = http_method + if new_sem_conv: + expected_attributes[ + SpanAttributes.HTTP_REQUEST_METHOD + ] = http_method self.assertEqual(span_list[0].attributes, expected_attributes) def test_basic_wsgi_call(self): @@ -190,6 +262,16 @@ def test_basic_wsgi_call(self): response = app(self.environ, self.start_response) self.validate_response(response) + def test_basic_wsgi_call_new_semconv(self): + app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) + response = app(self.environ, self.start_response) + self.validate_response(response, old_sem_conv=False, new_sem_conv=True) + + def test_basic_wsgi_call_both_semconv(self): + app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) + response = app(self.environ, self.start_response) + self.validate_response(response, old_sem_conv=True, new_sem_conv=True) + def test_hooks(self): hook_headers = ( "hook_attr", @@ -283,7 +365,7 @@ def test_wsgi_metrics(self): for scope_metric in resource_metric.scope_metrics: self.assertTrue(len(scope_metric.metrics) != 0) for metric in scope_metric.metrics: - self.assertIn(metric.name, _expected_metric_names) + self.assertIn(metric.name, _expected_metric_names_old) data_points = list(metric.data.data_points) self.assertEqual(len(data_points), 1) for point in data_points: @@ -294,7 +376,77 @@ def test_wsgi_metrics(self): number_data_point_seen = True for attr in point.attributes: self.assertIn( - attr, _recommended_attrs[metric.name] + attr, + _recommended_metrics_attrs_old[metric.name], + ) + self.assertTrue(number_data_point_seen and histogram_data_point_seen) + + def test_wsgi_metrics_new_semconv(self): + app = otel_wsgi.OpenTelemetryMiddleware(error_wsgi_unhandled) + self.assertRaises(ValueError, app, self.environ, self.start_response) + self.assertRaises(ValueError, app, self.environ, self.start_response) + self.assertRaises(ValueError, app, self.environ, self.start_response) + metrics_list = self.memory_metrics_reader.get_metrics_data() + number_data_point_seen = False + histogram_data_point_seen = False + + self.assertTrue(len(metrics_list.resource_metrics) != 0) + for resource_metric in metrics_list.resource_metrics: + self.assertTrue(len(resource_metric.scope_metrics) != 0) + for scope_metric in resource_metric.scope_metrics: + self.assertTrue(len(scope_metric.metrics) != 0) + for metric in scope_metric.metrics: + self.assertIn(metric.name, _expected_metric_names_new) + data_points = list(metric.data.data_points) + self.assertEqual(len(data_points), 1) + for point in data_points: + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 3) + histogram_data_point_seen = True + if isinstance(point, NumberDataPoint): + number_data_point_seen = True + for attr in point.attributes: + self.assertIn( + attr, + _recommended_metrics_attrs_new[metric.name], + ) + self.assertTrue(number_data_point_seen and histogram_data_point_seen) + + def test_wsgi_metrics_both_semconv(self): + app = otel_wsgi.OpenTelemetryMiddleware(error_wsgi_unhandled) + self.assertRaises(ValueError, app, self.environ, self.start_response) + metrics_list = self.memory_metrics_reader.get_metrics_data() + number_data_point_seen = False + histogram_data_point_seen = False + + self.assertTrue(len(metrics_list.resource_metrics) != 0) + for resource_metric in metrics_list.resource_metrics: + self.assertTrue(len(resource_metric.scope_metrics) != 0) + for scope_metric in resource_metric.scope_metrics: + self.assertTrue(len(scope_metric.metrics) != 0) + for metric in scope_metric.metrics: + if metric.unit == "ms": + self.assertEqual(metric.name, "http.server.duration") + elif metric.unit == "s": + self.assertEqual( + metric.name, "http.server.request.duration" + ) + else: + self.assertEqual( + metric.name, "http.server.active_requests" + ) + data_points = list(metric.data.data_points) + self.assertEqual(len(data_points), 1) + for point in data_points: + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 1) + histogram_data_point_seen = True + if isinstance(point, NumberDataPoint): + number_data_point_seen = True + for attr in point.attributes: + self.assertIn( + attr, + _recommended_metrics_attrs_both[metric.name], ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) @@ -303,7 +455,7 @@ def test_nonstandard_http_method(self): app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) response = app(self.environ, self.start_response) self.validate_response( - response, span_name="UNKNOWN /", http_method="UNKNOWN" + response, span_name="HTTP", http_method="_OTHER" ) @mock.patch.dict( @@ -349,34 +501,95 @@ def test_request_attributes(self): SpanAttributes.HTTP_SCHEME: "http", SpanAttributes.HTTP_SERVER_NAME: "127.0.0.1", SpanAttributes.HTTP_FLAVOR: "1.0", + SpanAttributes.NET_HOST_NAME: "127.0.0.1", + }, + ) + + def test_request_attributes_new_semconv(self): + self.environ["QUERY_STRING"] = "foo=bar" + self.environ["REQUEST_URI"] = "http://127.0.0.1/?foo=bar" + + attrs = otel_wsgi.collect_request_attributes( + self.environ, + _HTTPStabilityMode.HTTP, + ) + self.assertDictEqual( + attrs, + { + SpanAttributes.HTTP_REQUEST_METHOD: "GET", + SpanAttributes.SERVER_ADDRESS: "127.0.0.1", + SpanAttributes.SERVER_PORT: 80, + SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.0", + SpanAttributes.URL_PATH: "/", + SpanAttributes.URL_QUERY: "foo=bar", }, ) - def validate_url(self, expected_url, raw=False, has_host=True): + def validate_url( + self, + expected_url, + raw=False, + has_host=True, + old_semconv=True, + new_semconv=False, + ): parts = urlsplit(expected_url) - expected = { + expected_old = { SpanAttributes.HTTP_SCHEME: parts.scheme, SpanAttributes.NET_HOST_PORT: parts.port or (80 if parts.scheme == "http" else 443), SpanAttributes.HTTP_SERVER_NAME: parts.hostname, # Not true in the general case, but for all tests. } - if raw: - expected[SpanAttributes.HTTP_TARGET] = expected_url.split( - parts.netloc, 1 - )[1] - else: - expected[SpanAttributes.HTTP_URL] = expected_url - if has_host: - expected[SpanAttributes.HTTP_HOST] = parts.hostname + expected_new = { + SpanAttributes.SERVER_PORT: parts.port + or (80 if parts.scheme == "http" else 443), + SpanAttributes.SERVER_ADDRESS: parts.hostname, + SpanAttributes.URL_PATH: parts.path, + SpanAttributes.URL_QUERY: parts.query, + } + if old_semconv: + if raw: + expected_old[SpanAttributes.HTTP_TARGET] = expected_url.split( + parts.netloc, 1 + )[1] + else: + expected_old[SpanAttributes.HTTP_URL] = expected_url + if has_host: + expected_old[SpanAttributes.HTTP_HOST] = parts.hostname + if new_semconv: + if raw: + expected_new[SpanAttributes.URL_PATH] = expected_url.split( + parts.path, 1 + )[1] + if parts.query: + expected_new[ + SpanAttributes.URL_QUERY + ] = expected_url.split(parts.query, 1)[1] + else: + expected_new[SpanAttributes.HTTP_URL] = expected_url + if has_host: + expected_new[SpanAttributes.SERVER_ADDRESS] = parts.hostname attrs = otel_wsgi.collect_request_attributes(self.environ) self.assertGreaterEqual( - attrs.items(), expected.items(), expected_url + " expected." + attrs.items(), expected_old.items(), expected_url + " expected." ) def test_request_attributes_with_partial_raw_uri(self): - self.environ["RAW_URI"] = "/#top" - self.validate_url("http://127.0.0.1/#top", raw=True) + self.environ["RAW_URI"] = "/?foo=bar/#top" + self.validate_url("http://127.0.0.1/?foo=bar/#top", raw=True) + self.validate_url( + "http://127.0.0.1/?foo=bar/#top", + raw=True, + old_semconv=False, + new_semconv=True, + ) + self.validate_url( + "http://127.0.0.1/?foo=bar/#top", + raw=True, + old_semconv=True, + new_semconv=True, + ) def test_request_attributes_with_partial_raw_uri_and_nonstandard_port( self, @@ -385,18 +598,68 @@ def test_request_attributes_with_partial_raw_uri_and_nonstandard_port( del self.environ["HTTP_HOST"] self.environ["SERVER_PORT"] = "8080" self.validate_url("http://127.0.0.1:8080/?", raw=True, has_host=False) + self.validate_url( + "http://127.0.0.1:8080/?", + raw=True, + has_host=False, + old_semconv=False, + new_semconv=True, + ) + self.validate_url( + "http://127.0.0.1:8080/?", + raw=True, + has_host=False, + old_semconv=True, + new_semconv=True, + ) def test_https_uri_port(self): del self.environ["HTTP_HOST"] self.environ["SERVER_PORT"] = "443" self.environ["wsgi.url_scheme"] = "https" self.validate_url("https://127.0.0.1/", has_host=False) + self.validate_url( + "https://127.0.0.1/", + has_host=False, + old_semconv=False, + new_semconv=True, + ) + self.validate_url( + "https://127.0.0.1/", + has_host=False, + old_semconv=True, + new_semconv=True, + ) self.environ["SERVER_PORT"] = "8080" self.validate_url("https://127.0.0.1:8080/", has_host=False) + self.validate_url( + "https://127.0.0.1:8080/", + has_host=False, + old_semconv=False, + new_semconv=True, + ) + self.validate_url( + "https://127.0.0.1:8080/", + has_host=False, + old_semconv=True, + new_semconv=True, + ) self.environ["SERVER_PORT"] = "80" self.validate_url("https://127.0.0.1:80/", has_host=False) + self.validate_url( + "https://127.0.0.1:80/", + has_host=False, + old_semconv=False, + new_semconv=True, + ) + self.validate_url( + "https://127.0.0.1:80/", + has_host=False, + old_semconv=True, + new_semconv=True, + ) def test_http_uri_port(self): del self.environ["HTTP_HOST"] @@ -438,10 +701,10 @@ def test_request_attributes_with_faux_scheme_relative_raw_uri(self): def test_request_attributes_pathless(self): self.environ["RAW_URI"] = "" - expected = {SpanAttributes.HTTP_TARGET: ""} - self.assertGreaterEqual( - otel_wsgi.collect_request_attributes(self.environ).items(), - expected.items(), + self.assertIsNone( + otel_wsgi.collect_request_attributes(self.environ).get( + SpanAttributes.HTTP_TARGET + ) ) def test_request_attributes_with_full_request_uri(self): @@ -449,29 +712,58 @@ def test_request_attributes_with_full_request_uri(self): self.environ["REQUEST_METHOD"] = "CONNECT" self.environ[ "REQUEST_URI" - ] = "127.0.0.1:8080" # Might happen in a CONNECT request - expected = { + ] = "http://docs.python.org:80/3/library/urllib.parse.html?highlight=params#url-parsing" # Might happen in a CONNECT request + expected_old = { SpanAttributes.HTTP_HOST: "127.0.0.1:8080", - SpanAttributes.HTTP_TARGET: "127.0.0.1:8080", + SpanAttributes.HTTP_TARGET: "http://docs.python.org:80/3/library/urllib.parse.html?highlight=params#url-parsing", + } + expected_new = { + SpanAttributes.URL_PATH: "/3/library/urllib.parse.html", + SpanAttributes.URL_QUERY: "highlight=params", } self.assertGreaterEqual( otel_wsgi.collect_request_attributes(self.environ).items(), - expected.items(), + expected_old.items(), + ) + self.assertGreaterEqual( + otel_wsgi.collect_request_attributes( + self.environ, + _HTTPStabilityMode.HTTP, + ).items(), + expected_new.items(), ) def test_http_user_agent_attribute(self): self.environ["HTTP_USER_AGENT"] = "test-useragent" expected = {SpanAttributes.HTTP_USER_AGENT: "test-useragent"} + expected_new = {SpanAttributes.USER_AGENT_ORIGINAL: "test-useragent"} self.assertGreaterEqual( otel_wsgi.collect_request_attributes(self.environ).items(), expected.items(), ) + self.assertGreaterEqual( + otel_wsgi.collect_request_attributes( + self.environ, + _HTTPStabilityMode.HTTP, + ).items(), + expected_new.items(), + ) def test_response_attributes(self): otel_wsgi.add_response_attributes(self.span, "404 Not Found", {}) + otel_wsgi.add_response_attributes( + self.span, + "404 Not Found", + {}, + sem_conv_opt_in_mode=_HTTPStabilityMode.HTTP, + ) expected = (mock.call(SpanAttributes.HTTP_STATUS_CODE, 404),) - self.assertEqual(self.span.set_attribute.call_count, len(expected)) + expected_new = ( + mock.call(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, 404), + ) + self.assertEqual(self.span.set_attribute.call_count, 2) self.span.set_attribute.assert_has_calls(expected, any_order=True) + self.span.set_attribute.assert_has_calls(expected_new, any_order=True) def test_credential_removal(self): self.environ["HTTP_HOST"] = "username:password@mock" diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index fbfc92cf21..31c2486acc 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -16,13 +16,16 @@ import threading from enum import Enum +from opentelemetry.instrumentation.utils import http_status_to_status_code from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.trace.status import Status, StatusCode # TODO: will come through semconv package once updated _SPAN_ATTRIBUTES_ERROR_TYPE = "error.type" _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS = "network.peer.address" _SPAN_ATTRIBUTES_NETWORK_PEER_PORT = "network.peer.port" _METRIC_ATTRIBUTES_CLIENT_DURATION_NAME = "http.client.request.duration" +_METRIC_ATTRIBUTES_SERVER_DURATION_NAME = "http.server.request.duration" _client_duration_attrs_old = [ SpanAttributes.HTTP_STATUS_CODE, @@ -45,13 +48,117 @@ # SpanAttributes.URL_SCHEME, ] +_server_duration_attrs_old = [ + SpanAttributes.HTTP_METHOD, + SpanAttributes.HTTP_HOST, + SpanAttributes.HTTP_SCHEME, + SpanAttributes.HTTP_STATUS_CODE, + SpanAttributes.HTTP_FLAVOR, + SpanAttributes.HTTP_SERVER_NAME, + SpanAttributes.NET_HOST_NAME, + SpanAttributes.NET_HOST_PORT, +] + +_server_duration_attrs_new = [ + _SPAN_ATTRIBUTES_ERROR_TYPE, + SpanAttributes.HTTP_REQUEST_METHOD, + SpanAttributes.HTTP_RESPONSE_STATUS_CODE, + SpanAttributes.HTTP_ROUTE, + SpanAttributes.NETWORK_PROTOCOL_VERSION, + # TODO: Support opt-in for scheme in new semconv + # SpanAttributes.URL_SCHEME, +] + +_server_active_requests_count_attrs_old = [ + SpanAttributes.HTTP_METHOD, + SpanAttributes.HTTP_HOST, + SpanAttributes.HTTP_SCHEME, + SpanAttributes.HTTP_FLAVOR, + SpanAttributes.HTTP_SERVER_NAME, + SpanAttributes.NET_HOST_NAME, + SpanAttributes.NET_HOST_PORT, +] + +_server_active_requests_count_attrs_new = [ + SpanAttributes.HTTP_REQUEST_METHOD, + SpanAttributes.URL_SCHEME, +] + +OTEL_SEMCONV_STABILITY_OPT_IN = "OTEL_SEMCONV_STABILITY_OPT_IN" + + +class _OpenTelemetryStabilitySignalType: + HTTP = "http" + + +class _HTTPStabilityMode(Enum): + # http - emit the new, stable HTTP and networking conventions ONLY + HTTP = "http" + # http/dup - emit both the old and the stable HTTP and networking conventions + HTTP_DUP = "http/dup" + # default - continue emitting old experimental HTTP and networking conventions + DEFAULT = "default" + + +def _report_new(mode): + return mode.name != _HTTPStabilityMode.DEFAULT.name + + +def _report_old(mode): + return mode.name != _HTTPStabilityMode.HTTP.name -def _filter_duration_attrs(attrs, sem_conv_opt_in_mode): + +class _OpenTelemetrySemanticConventionStability: + _initialized = False + _lock = threading.Lock() + _OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING = {} + + @classmethod + def _initialize(cls): + with _OpenTelemetrySemanticConventionStability._lock: + if not _OpenTelemetrySemanticConventionStability._initialized: + # Users can pass in comma delimited string for opt-in options + # Only values for http stability are supported for now + opt_in = os.environ.get(OTEL_SEMCONV_STABILITY_OPT_IN, "") + opt_in_list = [] + if opt_in: + opt_in_list = [s.strip() for s in opt_in.split(",")] + http_opt_in = _HTTPStabilityMode.DEFAULT + if opt_in_list: + # Process http opt-in + # http/dup takes priority over http + if _HTTPStabilityMode.HTTP_DUP.value in opt_in_list: + http_opt_in = _HTTPStabilityMode.HTTP_DUP + elif _HTTPStabilityMode.HTTP.value in opt_in_list: + http_opt_in = _HTTPStabilityMode.HTTP + _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[ + _OpenTelemetryStabilitySignalType.HTTP + ] = http_opt_in + _OpenTelemetrySemanticConventionStability._initialized = True + + @classmethod + # Get OpenTelemetry opt-in mode based off of signal type (http, messaging, etc.) + def _get_opentelemetry_stability_opt_in_mode( + cls, + signal_type: _OpenTelemetryStabilitySignalType, + ) -> _HTTPStabilityMode: + return _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING.get( + signal_type, _HTTPStabilityMode.DEFAULT + ) + + +def _filter_semconv_duration_attrs( + attrs, + old_attrs, + new_attrs, + sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, +): filtered_attrs = {} + # duration is two different metrics depending on sem_conv_opt_in_mode, so no DUP attributes allowed_attributes = ( - _client_duration_attrs_new - if sem_conv_opt_in_mode == _OpenTelemetryStabilityMode.HTTP - else _client_duration_attrs_old + new_attrs + if sem_conv_opt_in_mode == _HTTPStabilityMode.HTTP + else old_attrs ) for key, val in attrs.items(): if key in allowed_attributes: @@ -59,6 +166,24 @@ def _filter_duration_attrs(attrs, sem_conv_opt_in_mode): return filtered_attrs +def _filter_semconv_active_request_count_attr( + attrs, + old_attrs, + new_attrs, + sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, +): + filtered_attrs = {} + if _report_old(sem_conv_opt_in_mode): + for key, val in attrs.items(): + if key in old_attrs: + filtered_attrs[key] = val + if _report_new(sem_conv_opt_in_mode): + for key, val in attrs.items(): + if key in new_attrs: + filtered_attrs[key] = val + return filtered_attrs + + def set_string_attribute(result, key, value): if value: result[key] = value @@ -90,6 +215,15 @@ def _set_http_method(result, original, normalized, sem_conv_opt_in_mode): ) +def _set_http_status_code(result, code, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_int_attribute(result, SpanAttributes.HTTP_STATUS_CODE, code) + if _report_new(sem_conv_opt_in_mode): + set_int_attribute( + result, SpanAttributes.HTTP_RESPONSE_STATUS_CODE, code + ) + + def _set_http_url(result, url, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_URL, url) @@ -105,36 +239,30 @@ def _set_http_scheme(result, scheme, sem_conv_opt_in_mode): # set_string_attribute(result, SpanAttributes.URL_SCHEME, scheme) -def _set_http_hostname(result, hostname, sem_conv_opt_in_mode): +def _set_http_host(result, host, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): - set_string_attribute(result, SpanAttributes.HTTP_HOST, hostname) + set_string_attribute(result, SpanAttributes.HTTP_HOST, host) if _report_new(sem_conv_opt_in_mode): - set_string_attribute(result, SpanAttributes.SERVER_ADDRESS, hostname) + set_string_attribute(result, SpanAttributes.SERVER_ADDRESS, host) + +# Client -def _set_http_net_peer_name(result, peer_name, sem_conv_opt_in_mode): + +def _set_http_net_peer_name_client(result, peer_name, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.NET_PEER_NAME, peer_name) if _report_new(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.SERVER_ADDRESS, peer_name) -def _set_http_port(result, port, sem_conv_opt_in_mode): +def _set_http_peer_port_client(result, port, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_int_attribute(result, SpanAttributes.NET_PEER_PORT, port) if _report_new(sem_conv_opt_in_mode): set_int_attribute(result, SpanAttributes.SERVER_PORT, port) -def _set_http_status_code(result, code, sem_conv_opt_in_mode): - if _report_old(sem_conv_opt_in_mode): - set_int_attribute(result, SpanAttributes.HTTP_STATUS_CODE, code) - if _report_new(sem_conv_opt_in_mode): - set_int_attribute( - result, SpanAttributes.HTTP_RESPONSE_STATUS_CODE, code - ) - - def _set_http_network_protocol_version(result, version, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_FLAVOR, version) @@ -144,74 +272,117 @@ def _set_http_network_protocol_version(result, version, sem_conv_opt_in_mode): ) -_OTEL_SEMCONV_STABILITY_OPT_IN_KEY = "OTEL_SEMCONV_STABILITY_OPT_IN" +# Server -class _OpenTelemetryStabilitySignalType: - HTTP = "http" +def _set_http_net_host(result, host, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.NET_HOST_NAME, host) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.SERVER_ADDRESS, host) -class _OpenTelemetryStabilityMode(Enum): - # http - emit the new, stable HTTP and networking conventions ONLY - HTTP = "http" - # http/dup - emit both the old and the stable HTTP and networking conventions - HTTP_DUP = "http/dup" - # default - continue emitting old experimental HTTP and networking conventions - DEFAULT = "default" +def _set_http_net_host_port(result, port, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_int_attribute(result, SpanAttributes.NET_HOST_PORT, port) + if _report_new(sem_conv_opt_in_mode): + set_int_attribute(result, SpanAttributes.SERVER_PORT, port) -def _report_new(mode): - return mode.name != _OpenTelemetryStabilityMode.DEFAULT.name +def _set_http_target(result, target, path, query, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.HTTP_TARGET, target) + if _report_new(sem_conv_opt_in_mode): + if path: + set_string_attribute(result, SpanAttributes.URL_PATH, path) + if query: + set_string_attribute(result, SpanAttributes.URL_QUERY, query) -def _report_old(mode): - return mode.name != _OpenTelemetryStabilityMode.HTTP.name +def _set_http_peer_ip(result, ip, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.NET_PEER_IP, ip) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.CLIENT_ADDRESS, ip) -class _OpenTelemetrySemanticConventionStability: - _initialized = False - _lock = threading.Lock() - _OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING = {} +def _set_http_peer_port_server(result, port, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_int_attribute(result, SpanAttributes.NET_PEER_PORT, port) + if _report_new(sem_conv_opt_in_mode): + set_int_attribute(result, SpanAttributes.CLIENT_PORT, port) - @classmethod - def _initialize(cls): - with _OpenTelemetrySemanticConventionStability._lock: - if not _OpenTelemetrySemanticConventionStability._initialized: - # Users can pass in comma delimited string for opt-in options - # Only values for http stability are supported for now - opt_in = os.environ.get(_OTEL_SEMCONV_STABILITY_OPT_IN_KEY, "") - opt_in_list = [] - if opt_in: - opt_in_list = [s.strip() for s in opt_in.split(",")] - http_opt_in = _OpenTelemetryStabilityMode.DEFAULT - if opt_in_list: - # Process http opt-in - # http/dup takes priority over http - if ( - _OpenTelemetryStabilityMode.HTTP_DUP.value - in opt_in_list - ): - http_opt_in = _OpenTelemetryStabilityMode.HTTP_DUP - elif _OpenTelemetryStabilityMode.HTTP.value in opt_in_list: - http_opt_in = _OpenTelemetryStabilityMode.HTTP - _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[ - _OpenTelemetryStabilitySignalType.HTTP - ] = http_opt_in - _OpenTelemetrySemanticConventionStability._initialized = True - @classmethod - # Get OpenTelemetry opt-in mode based off of signal type (http, messaging, etc.) - def _get_opentelemetry_stability_opt_in_mode( - cls, - signal_type: _OpenTelemetryStabilitySignalType, - ) -> _OpenTelemetryStabilityMode: - return _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING.get( - signal_type, _OpenTelemetryStabilityMode.DEFAULT +def _set_http_user_agent(result, user_agent, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute( + result, SpanAttributes.HTTP_USER_AGENT, user_agent + ) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute( + result, SpanAttributes.USER_AGENT_ORIGINAL, user_agent + ) + + +def _set_http_net_peer_name_server(result, name, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.NET_PEER_NAME, name) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.CLIENT_ADDRESS, name) + + +def _set_http_flavor_version(result, version, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.HTTP_FLAVOR, version) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute( + result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version + ) + + +def _set_status( + span, + metrics_attributes, + status_code_str, + status_code, + sem_conv_opt_in_mode, +): + if status_code < 0: + if _report_new(sem_conv_opt_in_mode): + span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, status_code_str) + metrics_attributes[_SPAN_ATTRIBUTES_ERROR_TYPE] = status_code_str + + span.set_status( + Status( + StatusCode.ERROR, + "Non-integer HTTP status: " + status_code_str, + ) ) + else: + status = http_status_to_status_code(status_code, server_span=True) + + if _report_old(sem_conv_opt_in_mode): + span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) + metrics_attributes[SpanAttributes.HTTP_STATUS_CODE] = status_code + if _report_new(sem_conv_opt_in_mode): + span.set_attribute( + SpanAttributes.HTTP_RESPONSE_STATUS_CODE, status_code + ) + metrics_attributes[ + SpanAttributes.HTTP_RESPONSE_STATUS_CODE + ] = status_code + if status == StatusCode.ERROR: + span.set_attribute( + _SPAN_ATTRIBUTES_ERROR_TYPE, status_code_str + ) + metrics_attributes[ + _SPAN_ATTRIBUTES_ERROR_TYPE + ] = status_code_str + span.set_status(Status(status)) # Get schema version based off of opt-in mode -def _get_schema_url(mode: _OpenTelemetryStabilityMode) -> str: - if mode is _OpenTelemetryStabilityMode.DEFAULT: +def _get_schema_url(mode: _HTTPStabilityMode) -> str: + if mode is _HTTPStabilityMode.DEFAULT: return "https://opentelemetry.io/schemas/1.11.0" return SpanAttributes.SCHEMA_URL diff --git a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py index 523f9400b1..1f7ce98937 100644 --- a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py +++ b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py @@ -218,7 +218,7 @@ def sanitize_method(method: Optional[str]) -> Optional[str]: ] ): return method - return "UNKNOWN" + return "_OTHER" def get_custom_headers(env_var: str) -> list[str]: @@ -245,3 +245,10 @@ def _parse_duration_attrs(req_attrs): for key in _duration_attrs.intersection(req_attrs.keys()) } return duration_attrs + + +def _parse_url_query(url: str): + parsed_url = urlparse(url) + path = parsed_url.path + query_params = parsed_url.query + return path, query_params From ad06e7043c6cde838ae0668ddf701827fca4cd51 Mon Sep 17 00:00:00 2001 From: Gal Bashan Date: Sun, 21 Apr 2024 14:11:39 +0300 Subject: [PATCH 14/58] feat(pika): adding support for channel.consume instrumentation (#2397) * feat(pika): adding support for channel.consume instrumentation * updated changelog * wip tests * updating docs * more tests * removing span member on object proxy * adding test for ReadyMessagesDequeProxy * adding tests * better comment on span.end() * fixing docs * ending span even on exceptions --- CHANGELOG.md | 7 ++ .../instrumentation/pika/__init__.py | 9 ++ .../instrumentation/pika/pika_instrumentor.py | 28 ++++- .../instrumentation/pika/utils.py | 83 ++++++++++++- .../tests/test_pika_instrumentation.py | 37 +++++- .../tests/test_utils.py | 116 ++++++++++++++++++ 6 files changed, 274 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6db0e04e68..193779659c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- `opentelemetry-instrumentation-pika` Instrumentation for `channel.consume()` (supported + only for global, non channel specific instrumentation) + ([#2397](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2397))) + + ### Breaking changes - Rename `type` attribute to `asgi.event.type` in `opentelemetry-instrumentation-asgi` diff --git a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/__init__.py b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/__init__.py index c745462cf3..d9cec06525 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/__init__.py @@ -77,6 +77,15 @@ def consume_hook(span: Span, body: bytes, properties: BasicProperties): PikaInstrumentor.instrument_channel(channel, publish_hook=publish_hook, consume_hook=consume_hook) +Consumer Instrumentation +------------------------ +For consumer instrumentation, pika supports two consuming modes: + +* Consumers using the `basic_consume` method which accepts a callback. This is supported for global instrumentation + (`PikaInstrumentor().instrument()`) as well channel specific instrumentation (`PikaInstrumentor().instrument_channel(channel)`) +* Consumers using the `consume` method which returns a generator over messages. This is supported for global + instrumentations only (`PikaInstrumentor().instrument()`) + API --- """ diff --git a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/pika_instrumentor.py b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/pika_instrumentor.py index 56c78a85c3..76261c89ce 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/pika_instrumentor.py +++ b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/pika_instrumentor.py @@ -20,7 +20,10 @@ import wrapt from packaging import version from pika.adapters import BlockingConnection -from pika.adapters.blocking_connection import BlockingChannel +from pika.adapters.blocking_connection import ( + BlockingChannel, + _QueueConsumerGeneratorInfo, +) from opentelemetry import trace from opentelemetry.instrumentation.instrumentor import BaseInstrumentor @@ -191,6 +194,24 @@ def wrapper(wrapped, instance, args, kwargs): wrapt.wrap_function_wrapper(channel, "basic_consume", wrapper) + @staticmethod + def _decorate_queue_consumer_generator( + tracer_provider: Optional[TracerProvider], + consume_hook: utils.HookT = utils.dummy_callback, + ) -> None: + tracer = trace.get_tracer(__name__, __version__, tracer_provider) + + def wrapper(wrapped, instance, args, kwargs): + res = wrapped(*args, **kwargs) + instance.pending_events = utils.ReadyMessagesDequeProxy( + instance.pending_events, instance, tracer, consume_hook + ) + return res + + wrapt.wrap_function_wrapper( + _QueueConsumerGeneratorInfo, "__init__", wrapper + ) + def _instrument(self, **kwargs: Dict[str, Any]) -> None: tracer_provider: TracerProvider = kwargs.get("tracer_provider", None) publish_hook: utils.HookT = kwargs.get( @@ -207,10 +228,15 @@ def _instrument(self, **kwargs: Dict[str, Any]) -> None: consume_hook=consume_hook, ) + self._decorate_queue_consumer_generator( + tracer_provider, consume_hook=consume_hook + ) + def _uninstrument(self, **kwargs: Dict[str, Any]) -> None: if hasattr(self, "__opentelemetry_tracer_provider"): delattr(self, "__opentelemetry_tracer_provider") unwrap(BlockingConnection, "channel") + unwrap(_QueueConsumerGeneratorInfo, "__init__") def instrumentation_dependencies(self) -> Collection[str]: return _instruments diff --git a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/utils.py b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/utils.py index 6dab4fdfa9..5afa5d9ee6 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/utils.py +++ b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/utils.py @@ -1,8 +1,13 @@ from logging import getLogger from typing import Any, Callable, List, Optional +from pika.adapters.blocking_connection import ( + _ConsumerDeliveryEvt, + _QueueConsumerGeneratorInfo, +) from pika.channel import Channel from pika.spec import Basic, BasicProperties +from wrapt import ObjectProxy from opentelemetry import context, propagate, trace from opentelemetry.instrumentation.utils import is_instrumentation_enabled @@ -128,7 +133,7 @@ def decorated_function( def _get_span( tracer: Tracer, - channel: Channel, + channel: Optional[Channel], properties: BasicProperties, task_name: str, destination: str, @@ -157,7 +162,7 @@ def _generate_span_name( def _enrich_span( span: Span, - channel: Channel, + channel: Optional[Channel], properties: BasicProperties, task_destination: str, operation: Optional[MessagingOperationValues] = None, @@ -176,6 +181,8 @@ def _enrich_span( span.set_attribute( SpanAttributes.MESSAGING_CONVERSATION_ID, properties.correlation_id ) + if not channel: + return if not hasattr(channel.connection, "params"): span.set_attribute( SpanAttributes.NET_PEER_NAME, channel.connection._impl.params.host @@ -190,3 +197,75 @@ def _enrich_span( span.set_attribute( SpanAttributes.NET_PEER_PORT, channel.connection.params.port ) + + +# pylint:disable=abstract-method +class ReadyMessagesDequeProxy(ObjectProxy): + def __init__( + self, + wrapped, + queue_consumer_generator: _QueueConsumerGeneratorInfo, + tracer: Optional[Tracer], + consume_hook: HookT = dummy_callback, + ): + super().__init__(wrapped) + self._self_active_token = None + self._self_tracer = tracer + self._self_consume_hook = consume_hook + self._self_queue_consumer_generator = queue_consumer_generator + + def popleft(self, *args, **kwargs): + try: + # end active context if exists + if self._self_active_token: + context.detach(self._self_active_token) + except Exception as inst_exception: # pylint: disable=W0703 + _LOG.exception(inst_exception) + + evt = self.__wrapped__.popleft(*args, **kwargs) + + try: + # If a new message was received, create a span and set as active context + if isinstance(evt, _ConsumerDeliveryEvt): + method = evt.method + properties = evt.properties + if not properties: + properties = BasicProperties(headers={}) + if properties.headers is None: + properties.headers = {} + ctx = propagate.extract( + properties.headers, getter=_pika_getter + ) + if not ctx: + ctx = context.get_current() + message_ctx_token = context.attach(ctx) + span = _get_span( + self._self_tracer, + None, + properties, + destination=method.exchange + if method.exchange + else method.routing_key, + span_kind=SpanKind.CONSUMER, + task_name=self._self_queue_consumer_generator.consumer_tag, + operation=MessagingOperationValues.RECEIVE, + ) + try: + context.detach(message_ctx_token) + self._self_active_token = context.attach( + trace.set_span_in_context(span) + ) + self._self_consume_hook(span, evt.body, properties) + except Exception as hook_exception: # pylint: disable=W0703 + _LOG.exception(hook_exception) + finally: + # We must end the span here, because the next place we can hook + # is not the end of the user code, but only when the next message + # arrives. we still set this span's context as the active context + # so spans created by user code that handles this message will be + # children of this one. + span.end() + except Exception as inst_exception: # pylint: disable=W0703 + _LOG.exception(inst_exception) + + return evt diff --git a/instrumentation/opentelemetry-instrumentation-pika/tests/test_pika_instrumentation.py b/instrumentation/opentelemetry-instrumentation-pika/tests/test_pika_instrumentation.py index 6e154c04f9..ad519c4a35 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/tests/test_pika_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-pika/tests/test_pika_instrumentation.py @@ -14,6 +14,7 @@ from unittest import TestCase, mock from pika.adapters import BlockingConnection +from pika.adapters.blocking_connection import _QueueConsumerGeneratorInfo from pika.channel import Channel from wrapt import BoundFunctionWrapper @@ -21,7 +22,10 @@ from opentelemetry.instrumentation.pika.pika_instrumentor import ( _consumer_callback_attribute_name, ) -from opentelemetry.instrumentation.pika.utils import dummy_callback +from opentelemetry.instrumentation.pika.utils import ( + ReadyMessagesDequeProxy, + dummy_callback, +) from opentelemetry.trace import Tracer @@ -40,13 +44,23 @@ def test_instrument_api(self) -> None: self.assertTrue( isinstance(BlockingConnection.channel, BoundFunctionWrapper) ) + self.assertTrue( + isinstance( + _QueueConsumerGeneratorInfo.__init__, BoundFunctionWrapper + ) + ) assert hasattr( instrumentation, "__opentelemetry_tracer_provider" ), "Tracer not stored for the object!" - instrumentation.uninstrument(channel=self.channel) + instrumentation.uninstrument() self.assertFalse( isinstance(BlockingConnection.channel, BoundFunctionWrapper) ) + self.assertFalse( + isinstance( + _QueueConsumerGeneratorInfo.__init__, BoundFunctionWrapper + ) + ) @mock.patch( "opentelemetry.instrumentation.pika.PikaInstrumentor._instrument_channel_functions" @@ -57,7 +71,7 @@ def test_instrument_api(self) -> None: @mock.patch( "opentelemetry.instrumentation.pika.PikaInstrumentor._instrument_blocking_channel_consumers" ) - def test_instrument( + def test_instrument_channel( self, instrument_blocking_channel_consumers: mock.MagicMock, instrument_basic_consume: mock.MagicMock, @@ -110,6 +124,23 @@ def test_instrument_basic_publish( self.channel.basic_publish, decorate_basic_publish.return_value ) + def test_instrument_queue_consumer_generator(self) -> None: + instrumentation = PikaInstrumentor() + instrumentation.instrument() + generator_info = _QueueConsumerGeneratorInfo( + params=("queue", False, False), consumer_tag="tag" + ) + self.assertTrue( + isinstance(generator_info.pending_events, ReadyMessagesDequeProxy) + ) + instrumentation.uninstrument() + generator_info = _QueueConsumerGeneratorInfo( + params=("queue", False, False), consumer_tag="tag" + ) + self.assertFalse( + isinstance(generator_info.pending_events, ReadyMessagesDequeProxy) + ) + def test_uninstrument_channel_functions(self) -> None: original_function = self.channel.basic_publish self.channel.basic_publish = mock.MagicMock() diff --git a/instrumentation/opentelemetry-instrumentation-pika/tests/test_utils.py b/instrumentation/opentelemetry-instrumentation-pika/tests/test_utils.py index ed33593389..d651ea64c9 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/tests/test_utils.py +++ b/instrumentation/opentelemetry-instrumentation-pika/tests/test_utils.py @@ -11,8 +11,14 @@ # 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 collections from unittest import TestCase, mock +from pika.adapters.blocking_connection import ( + _ConsumerCancellationEvt, + _ConsumerDeliveryEvt, + _QueueConsumerGeneratorInfo, +) from pika.channel import Channel from pika.spec import Basic, BasicProperties @@ -448,3 +454,113 @@ def test_decorate_basic_publish_when_span_is_not_recording( exchange_name, routing_key, mock_body, properties, False ) self.assertEqual(retval, callback.return_value) + + # pylint: disable=too-many-statements + @mock.patch("opentelemetry.instrumentation.pika.utils._get_span") + @mock.patch("opentelemetry.propagate.extract") + @mock.patch("opentelemetry.context.detach") + @mock.patch("opentelemetry.context.attach") + @mock.patch("opentelemetry.context.get_current") + def test_decorate_deque_proxy( + self, + context_get_current: mock.MagicMock, + context_attach: mock.MagicMock, + context_detach: mock.MagicMock, + extract: mock.MagicMock, + get_span: mock.MagicMock, + ) -> None: + returned_span = mock.MagicMock() + get_span.return_value = returned_span + consume_hook = mock.MagicMock() + tracer = mock.MagicMock() + generator_info = mock.MagicMock( + spec=_QueueConsumerGeneratorInfo, + pending_events=mock.MagicMock(spec=collections.deque), + consumer_tag="mock_task_name", + ) + method = mock.MagicMock(spec=Basic.Deliver) + method.exchange = "test_exchange" + properties = mock.MagicMock() + evt = _ConsumerDeliveryEvt(method, properties, b"mock_body") + generator_info.pending_events.popleft.return_value = evt + proxy = utils.ReadyMessagesDequeProxy( + generator_info.pending_events, generator_info, tracer, consume_hook + ) + + # First call (no detach cleanup) + res = proxy.popleft() + self.assertEqual(res, evt) + generator_info.pending_events.popleft.assert_called_once() + extract.assert_called_once_with( + properties.headers, getter=utils._pika_getter + ) + context_get_current.assert_called_once() + self.assertEqual(context_attach.call_count, 2) + self.assertEqual(context_detach.call_count, 1) + get_span.assert_called_once_with( + tracer, + None, + properties, + destination=method.exchange, + span_kind=SpanKind.CONSUMER, + task_name=generator_info.consumer_tag, + operation=MessagingOperationValues.RECEIVE, + ) + consume_hook.assert_called_once() + returned_span.end.assert_called_once() + + generator_info.pending_events.reset_mock() + extract.reset_mock() + context_get_current.reset_mock() + get_span.reset_mock() + context_attach.reset_mock() + context_detach.reset_mock() + returned_span.end.reset_mock() + consume_hook.reset_mock() + + # Second call (has detach cleanup) + res = proxy.popleft() + self.assertEqual(res, evt) + generator_info.pending_events.popleft.assert_called_once() + extract.assert_called_once_with( + properties.headers, getter=utils._pika_getter + ) + context_get_current.assert_called_once() + self.assertEqual(context_attach.call_count, 2) + self.assertEqual(context_detach.call_count, 2) + get_span.assert_called_once_with( + tracer, + None, + properties, + destination=method.exchange, + span_kind=SpanKind.CONSUMER, + task_name=generator_info.consumer_tag, + operation=MessagingOperationValues.RECEIVE, + ) + consume_hook.assert_called_once() + returned_span.end.assert_called_once() + generator_info.pending_events.reset_mock() + + extract.reset_mock() + context_get_current.reset_mock() + get_span.reset_mock() + context_attach.reset_mock() + context_detach.reset_mock() + returned_span.end.reset_mock() + consume_hook.reset_mock() + + # Third call (cancellation event) + evt = _ConsumerCancellationEvt("") + generator_info.pending_events.popleft.return_value = evt + + res = proxy.popleft() + + self.assertEqual(res, evt) + generator_info.pending_events.popleft.assert_called_once() + extract.assert_not_called() + context_get_current.not_called() + context_detach.assert_called_once() + context_attach.assert_not_called() + get_span.assert_not_called() + consume_hook.assert_not_called() + returned_span.end.assert_not_called() From 7f4853ac798547fd78ead4eaa33fab591996011b Mon Sep 17 00:00:00 2001 From: Severin Neumann Date: Mon, 22 Apr 2024 20:23:38 +0200 Subject: [PATCH 15/58] fix: revert modifications to Apache license (#2429) * fix: revert modifications to Apache license See open-telemetry/community#2056 for background * path all LICENSE files Signed-off-by: svrnm * Fix missing LICENSE files --------- Signed-off-by: svrnm Co-authored-by: Diego Hurtado --- LICENSE | 2 +- LICENSE.Apache | 5 +++-- .../opentelemetry-instrumentation-aio-pika/LICENSE | 2 +- .../opentelemetry-instrumentation-aiohttp-client/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-aiopg/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-asgi/LICENSE | 2 +- .../opentelemetry-instrumentation-asyncio/LICENSE | 2 +- .../opentelemetry-instrumentation-asyncpg/LICENSE | 2 +- .../opentelemetry-instrumentation-aws-lambda/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-boto/LICENSE | 2 +- .../opentelemetry-instrumentation-boto3sqs/LICENSE | 2 +- .../opentelemetry-instrumentation-botocore/LICENSE | 2 +- .../opentelemetry-instrumentation-cassandra/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-celery/LICENSE | 2 +- .../opentelemetry-instrumentation-confluent-kafka/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-dbapi/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-django/LICENSE | 2 +- .../opentelemetry-instrumentation-elasticsearch/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-falcon/LICENSE | 2 +- .../opentelemetry-instrumentation-fastapi/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-flask/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-grpc/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-httpx/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-jinja2/LICENSE | 2 +- .../opentelemetry-instrumentation-kafka-python/LICENSE | 2 +- .../opentelemetry-instrumentation-logging/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-mysql/LICENSE | 2 +- .../opentelemetry-instrumentation-mysqlclient/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-pika/LICENSE | 2 +- .../opentelemetry-instrumentation-psycopg/LICENSE | 2 +- .../opentelemetry-instrumentation-psycopg2/LICENSE | 2 +- .../opentelemetry-instrumentation-pymemcache/LICENSE | 2 +- .../opentelemetry-instrumentation-pymongo/LICENSE | 2 +- .../opentelemetry-instrumentation-pymysql/LICENSE | 2 +- .../opentelemetry-instrumentation-pyramid/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-redis/LICENSE | 2 +- .../opentelemetry-instrumentation-remoulade/LICENSE | 2 +- .../opentelemetry-instrumentation-requests/LICENSE | 2 +- .../opentelemetry-instrumentation-sklearn/LICENSE | 2 +- .../opentelemetry-instrumentation-sqlalchemy/LICENSE | 2 +- .../opentelemetry-instrumentation-sqlite3/LICENSE | 2 +- .../opentelemetry-instrumentation-starlette/LICENSE | 2 +- .../opentelemetry-instrumentation-threading/LICENSE | 2 +- .../opentelemetry-instrumentation-tornado/LICENSE | 2 +- .../opentelemetry-instrumentation-tortoiseorm/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-urllib/LICENSE | 2 +- .../opentelemetry-instrumentation-urllib3/LICENSE | 2 +- instrumentation/opentelemetry-instrumentation-wsgi/LICENSE | 2 +- opentelemetry-contrib-instrumentations/LICENSE | 2 +- opentelemetry-instrumentation/LICENSE | 2 +- propagator/opentelemetry-propagator-aws-xray/LICENSE | 2 +- resource/opentelemetry-resource-detector-azure/LICENSE | 2 +- resource/opentelemetry-resource-detector-container/LICENSE | 2 +- sdk-extension/opentelemetry-sdk-extension-aws/LICENSE | 2 +- 54 files changed, 56 insertions(+), 55 deletions(-) diff --git a/LICENSE b/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/LICENSE.Apache b/LICENSE.Apache index bff56b5431..261eeb9e9f 100644 --- a/LICENSE.Apache +++ b/LICENSE.Apache @@ -178,7 +178,7 @@ 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 "{}" + 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 @@ -186,7 +186,8 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2016 Datadog, Inc. + 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 diff --git a/instrumentation/opentelemetry-instrumentation-aio-pika/LICENSE b/instrumentation/opentelemetry-instrumentation-aio-pika/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-aio-pika/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-aio-pika/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/LICENSE b/instrumentation/opentelemetry-instrumentation-aiohttp-client/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/LICENSE b/instrumentation/opentelemetry-instrumentation-aiopg/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-aiopg/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-asgi/LICENSE b/instrumentation/opentelemetry-instrumentation-asgi/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-asgi/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/LICENSE b/instrumentation/opentelemetry-instrumentation-asyncio/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncio/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-asyncio/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/LICENSE b/instrumentation/opentelemetry-instrumentation-asyncpg/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncpg/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-asyncpg/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/LICENSE b/instrumentation/opentelemetry-instrumentation-aws-lambda/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-boto/LICENSE b/instrumentation/opentelemetry-instrumentation-boto/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-boto/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-boto/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-boto3sqs/LICENSE b/instrumentation/opentelemetry-instrumentation-boto3sqs/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-boto3sqs/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-boto3sqs/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-botocore/LICENSE b/instrumentation/opentelemetry-instrumentation-botocore/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-botocore/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-cassandra/LICENSE b/instrumentation/opentelemetry-instrumentation-cassandra/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-cassandra/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-cassandra/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-celery/LICENSE b/instrumentation/opentelemetry-instrumentation-celery/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-celery/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-celery/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-confluent-kafka/LICENSE b/instrumentation/opentelemetry-instrumentation-confluent-kafka/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-confluent-kafka/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-confluent-kafka/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/LICENSE b/instrumentation/opentelemetry-instrumentation-dbapi/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-dbapi/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-dbapi/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-django/LICENSE b/instrumentation/opentelemetry-instrumentation-django/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-django/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-django/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/LICENSE b/instrumentation/opentelemetry-instrumentation-elasticsearch/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-falcon/LICENSE b/instrumentation/opentelemetry-instrumentation-falcon/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-falcon/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/LICENSE b/instrumentation/opentelemetry-instrumentation-fastapi/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-fastapi/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-fastapi/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-flask/LICENSE b/instrumentation/opentelemetry-instrumentation-flask/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-flask/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-grpc/LICENSE b/instrumentation/opentelemetry-instrumentation-grpc/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-grpc/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-httpx/LICENSE b/instrumentation/opentelemetry-instrumentation-httpx/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-httpx/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-jinja2/LICENSE b/instrumentation/opentelemetry-instrumentation-jinja2/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-jinja2/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-jinja2/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-kafka-python/LICENSE b/instrumentation/opentelemetry-instrumentation-kafka-python/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-kafka-python/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-kafka-python/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-logging/LICENSE b/instrumentation/opentelemetry-instrumentation-logging/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-logging/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-logging/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-mysql/LICENSE b/instrumentation/opentelemetry-instrumentation-mysql/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-mysql/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-mysql/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-mysqlclient/LICENSE b/instrumentation/opentelemetry-instrumentation-mysqlclient/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-mysqlclient/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-mysqlclient/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-pika/LICENSE b/instrumentation/opentelemetry-instrumentation-pika/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-pika/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-psycopg/LICENSE b/instrumentation/opentelemetry-instrumentation-psycopg/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-psycopg/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/LICENSE b/instrumentation/opentelemetry-instrumentation-psycopg2/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-psycopg2/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/LICENSE b/instrumentation/opentelemetry-instrumentation-pymemcache/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-pymemcache/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/LICENSE b/instrumentation/opentelemetry-instrumentation-pymongo/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-pymongo/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-pymongo/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/LICENSE b/instrumentation/opentelemetry-instrumentation-pymysql/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-pymysql/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-pymysql/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/LICENSE b/instrumentation/opentelemetry-instrumentation-pyramid/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-pyramid/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-redis/LICENSE b/instrumentation/opentelemetry-instrumentation-redis/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-redis/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-remoulade/LICENSE b/instrumentation/opentelemetry-instrumentation-remoulade/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-remoulade/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-remoulade/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-requests/LICENSE b/instrumentation/opentelemetry-instrumentation-requests/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-requests/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-sklearn/LICENSE b/instrumentation/opentelemetry-instrumentation-sklearn/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-sklearn/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-sklearn/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/LICENSE b/instrumentation/opentelemetry-instrumentation-sqlalchemy/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/LICENSE b/instrumentation/opentelemetry-instrumentation-sqlite3/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-sqlite3/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-starlette/LICENSE b/instrumentation/opentelemetry-instrumentation-starlette/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-starlette/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-starlette/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-threading/LICENSE b/instrumentation/opentelemetry-instrumentation-threading/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-threading/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-tornado/LICENSE b/instrumentation/opentelemetry-instrumentation-tornado/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-tornado/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-tortoiseorm/LICENSE b/instrumentation/opentelemetry-instrumentation-tortoiseorm/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-tortoiseorm/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-tortoiseorm/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-urllib/LICENSE b/instrumentation/opentelemetry-instrumentation-urllib/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-urllib/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-urllib3/LICENSE b/instrumentation/opentelemetry-instrumentation-urllib3/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib3/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-urllib3/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/LICENSE b/instrumentation/opentelemetry-instrumentation-wsgi/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/LICENSE +++ b/instrumentation/opentelemetry-instrumentation-wsgi/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/opentelemetry-contrib-instrumentations/LICENSE b/opentelemetry-contrib-instrumentations/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/opentelemetry-contrib-instrumentations/LICENSE +++ b/opentelemetry-contrib-instrumentations/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/opentelemetry-instrumentation/LICENSE b/opentelemetry-instrumentation/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/opentelemetry-instrumentation/LICENSE +++ b/opentelemetry-instrumentation/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/propagator/opentelemetry-propagator-aws-xray/LICENSE b/propagator/opentelemetry-propagator-aws-xray/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/propagator/opentelemetry-propagator-aws-xray/LICENSE +++ b/propagator/opentelemetry-propagator-aws-xray/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/resource/opentelemetry-resource-detector-azure/LICENSE b/resource/opentelemetry-resource-detector-azure/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/resource/opentelemetry-resource-detector-azure/LICENSE +++ b/resource/opentelemetry-resource-detector-azure/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/resource/opentelemetry-resource-detector-container/LICENSE b/resource/opentelemetry-resource-detector-container/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/resource/opentelemetry-resource-detector-container/LICENSE +++ b/resource/opentelemetry-resource-detector-container/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. diff --git a/sdk-extension/opentelemetry-sdk-extension-aws/LICENSE b/sdk-extension/opentelemetry-sdk-extension-aws/LICENSE index 1ef7dad2c5..261eeb9e9f 100644 --- a/sdk-extension/opentelemetry-sdk-extension-aws/LICENSE +++ b/sdk-extension/opentelemetry-sdk-extension-aws/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright The OpenTelemetry Authors + 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. From 2718ffc7a734bb1f74be5ee89add5ab15bfc29b7 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Mon, 22 Apr 2024 13:48:51 -0500 Subject: [PATCH 16/58] Fix elasticsearch test requirements file name (#2443) Fixes #2442 Co-authored-by: Leighton Chen --- ...{test-requirements-6.txt => test-requirements.txt} | 0 tox.ini | 11 ++++------- 2 files changed, 4 insertions(+), 7 deletions(-) rename instrumentation/opentelemetry-instrumentation-elasticsearch/{test-requirements-6.txt => test-requirements.txt} (100%) diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-6.txt b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements.txt similarity index 100% rename from instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-6.txt rename to instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements.txt diff --git a/tox.ini b/tox.ini index bee544cb70..e48cc7bfe2 100644 --- a/tox.ini +++ b/tox.ini @@ -76,11 +76,8 @@ envlist = ; opentelemetry-instrumentation-elasticsearch ; FIXME: Elasticsearch >=7 causes CI workflow tests to hang, see open-telemetry/opentelemetry-python-contrib#620 - ; The numbers at the end of the environment names - ; below mean these dependencies are being used: - ; 6: elasticsearch-dsl>=6.0,<7.0 elasticsearch>=6.0,<7.0 - py3{8,9,10,11}-test-instrumentation-elasticsearch-6 - pypy3-test-instrumentation-elasticsearch-6 + py3{8,9,10,11}-test-instrumentation-elasticsearch + pypy3-test-instrumentation-elasticsearch ; opentelemetry-instrumentation-falcon ; py310 does not work with falcon 1 @@ -448,7 +445,7 @@ commands_pre = sqlalchemy-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-0.txt sqlalchemy-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-1.txt - elasticsearch-6: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-6.txt + elasticsearch: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements.txt asyncio: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncio/test-requirements.txt @@ -611,7 +608,7 @@ commands_pre = # prerequisite: follow the instructions here https://github.com/PyMySQL/mysqlclient#install # for your OS to install the required dependencies pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-mysqlclient/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-6.txt + pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements.txt pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-tornado/test-requirements.txt pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements.txt pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymongo/test-requirements.txt From 4e90498bf394f3c4f6646122cf624304ac8ebc45 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Mon, 22 Apr 2024 15:34:27 -0500 Subject: [PATCH 17/58] Update action versions (#2441) Fixes #2440 Co-authored-by: Leighton Chen --- .github/workflows/backport.yml | 4 ++-- .github/workflows/changelog.yml | 4 ++-- .github/workflows/codeql-analysis.yml | 8 ++++---- .github/workflows/instrumentations_0.yml | 6 +++--- .github/workflows/instrumentations_1.yml | 6 +++--- .github/workflows/prepare-patch-release.yml | 6 +++--- .github/workflows/prepare-release-branch.yml | 12 ++++++------ .github/workflows/publish-a-package-from-tag.yml | 4 ++-- .github/workflows/release.yml | 10 +++++----- .github/workflows/test.yml | 6 +++--- 10 files changed, 33 insertions(+), 33 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index f308c5757b..26789093f2 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -16,7 +16,7 @@ jobs: exit 1 fi - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: # history is needed to run git cherry-pick below fetch-depth: 0 @@ -40,4 +40,4 @@ jobs: gh pr create --title "[$GITHUB_REF_NAME] $title" \ --body "Clean cherry-pick of #$NUMBER to the \`$GITHUB_REF_NAME\` branch." \ --head $branch \ - --base $GITHUB_REF_NAME \ No newline at end of file + --base $GITHUB_REF_NAME diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 5238e01c4b..491ddd27fa 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -18,7 +18,7 @@ jobs: && github.actor != 'opentelemetrybot' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Check for CHANGELOG changes run: | @@ -33,4 +33,4 @@ jobs: echo "No CHANGELOG was modified." echo "Please add a CHANGELOG entry, or add the \"Skip Changelog\" label if not required." false - fi \ No newline at end of file + fi diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b979a3121b..8ef01d21cb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -20,16 +20,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v3 with: languages: python - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 \ No newline at end of file + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/instrumentations_0.yml b/.github/workflows/instrumentations_0.yml index 256d160640..a138621633 100644 --- a/.github/workflows/instrumentations_0.yml +++ b/.github/workflows/instrumentations_0.yml @@ -104,16 +104,16 @@ jobs: package: "grpc" steps: - name: Checkout Contrib Repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Python ${{ env[matrix.python-version] }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ env[matrix.python-version] }} - name: Install tox run: pip install tox - name: Cache tox environment # Preserves .tox directory between runs for faster installs - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: | .tox diff --git a/.github/workflows/instrumentations_1.yml b/.github/workflows/instrumentations_1.yml index 59db21529a..904f2ee999 100644 --- a/.github/workflows/instrumentations_1.yml +++ b/.github/workflows/instrumentations_1.yml @@ -43,16 +43,16 @@ jobs: package: "prometheus-remote-write" steps: - name: Checkout Contrib Repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Python ${{ env[matrix.python-version] }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ env[matrix.python-version] }} - name: Install tox run: pip install tox - name: Cache tox environment # Preserves .tox directory between runs for faster installs - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: | .tox diff --git a/.github/workflows/prepare-patch-release.yml b/.github/workflows/prepare-patch-release.yml index 49b9c89560..7c854d436d 100644 --- a/.github/workflows/prepare-patch-release.yml +++ b/.github/workflows/prepare-patch-release.yml @@ -6,7 +6,7 @@ jobs: prepare-patch-release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: | if [[ ! $GITHUB_REF_NAME =~ ^release/v[0-9]+\.[0-9]+\.x-0\.[0-9]+bx$ ]]; then @@ -50,7 +50,7 @@ jobs: run: .github/scripts/update-version.sh $STABLE_VERSION $UNSTABLE_VERSION - name: Set up Python 3.9 - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: 3.9 - name: Install tox @@ -79,4 +79,4 @@ jobs: gh pr create --title "[$GITHUB_REF_NAME] $message" \ --body "$message." \ --head $branch \ - --base $GITHUB_REF_NAME \ No newline at end of file + --base $GITHUB_REF_NAME diff --git a/.github/workflows/prepare-release-branch.yml b/.github/workflows/prepare-release-branch.yml index a4caf86ebe..6818772acf 100644 --- a/.github/workflows/prepare-release-branch.yml +++ b/.github/workflows/prepare-release-branch.yml @@ -10,7 +10,7 @@ jobs: prereqs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Verify prerequisites env: @@ -39,7 +39,7 @@ jobs: runs-on: ubuntu-latest needs: prereqs steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Create release branch env: @@ -77,7 +77,7 @@ jobs: run: .github/scripts/update-version.sh $STABLE_VERSION $UNSTABLE_VERSION - name: Set up Python 3.9 - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: 3.9 - name: Install tox @@ -112,7 +112,7 @@ jobs: runs-on: ubuntu-latest needs: prereqs steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set environment variables env: @@ -161,7 +161,7 @@ jobs: run: .github/scripts/update-version.sh $STABLE_NEXT_VERSION $UNSTABLE_NEXT_VERSION - name: Set up Python 3.9 - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: 3.9 - name: Install tox @@ -192,4 +192,4 @@ jobs: gh pr create --title "$message" \ --body "$body" \ --head $branch \ - --base main \ No newline at end of file + --base main diff --git a/.github/workflows/publish-a-package-from-tag.yml b/.github/workflows/publish-a-package-from-tag.yml index 2c07bf5d7a..a64f5fcf15 100644 --- a/.github/workflows/publish-a-package-from-tag.yml +++ b/.github/workflows/publish-a-package-from-tag.yml @@ -10,8 +10,8 @@ jobs: name: Publish package from tag runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - uses: actions/setup-python@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: '3.9' - name: Log tag that triggered publish workflow diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f6c267003a..b60ebc7599 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: exit 1 fi - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set environment variables run: | @@ -56,15 +56,15 @@ jobs: # check out main branch to verify there won't be problems with merging the change log # at the end of this workflow - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: main # back to the release branch - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # next few steps publish to pypi - - uses: actions/setup-python@v1 + - uses: actions/setup-python@v5 with: python-version: '3.8' @@ -127,7 +127,7 @@ jobs: --discussion-category announcements \ v$UNSTABLE_VERSION - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: # the step below is creating a pull request against main ref: main diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 10c648e517..529f70e565 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,9 +18,9 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout Contrib Repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Python 3.10 - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: "3.10" - name: Install tox @@ -30,7 +30,7 @@ jobs: run: sudo apt-get install -y libsnappy-dev - name: Cache tox environment # Preserves .tox directory between runs for faster installs - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: | .tox From c644f0d7d5f4c8bbb9f89a49f84c0dca2fcc2e7f Mon Sep 17 00:00:00 2001 From: Matt Oberle Date: Mon, 22 Apr 2024 18:44:31 -0400 Subject: [PATCH 18/58] [boto3sqs] Instrument `Session` and `resource` (#2161) * [boto3sqs] Instrument `Session` and `resource` This commit addresses the following open issues: - https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1699 - https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1996 There are four ways to access the SQS API via `boto3`: - `client = boto3.client("sqs")` - `client = boto3.Session().client("sqs")` - `sqs = boto3.resource("sqs")` - `sqs = boto3.Session().resource("sqs")` The existing wrapper tied into `boto3.client` to wrap a generated `botocore.client.SQS` class. The change here covers the three missing initialization methods. * update changelog * rename duplicate test methods * implement uninstrument * [boto3sqs] Reduce number of wrapper targets There are actually 6 ways to initialize a boto3 API object. ```py boto3.client() # Using default global session boto3.resource() # Using default global session boto3.Session().client() # Using "re-exported" session.Session boto3.Session().resource() # Using "re-exported" session.Session boto3.session.Session().client() # Using session.Session directly boto3.session.Session().resource() # Using session.Session directly ``` We only have to patch `session.Session.client` to catch all the cases. - https://github.com/boto/boto3/blob/b3c158c62aa2a1314dc0ec78caea1ea976abd1a0/boto3/session.py#L217-L229 - https://github.com/boto/boto3/blob/b3c158c62aa2a1314dc0ec78caea1ea976abd1a0/boto3/session.py#L446-L457 * Remove unused import --------- Co-authored-by: Matt Oberle Co-authored-by: Diego Hurtado --- CHANGELOG.md | 2 + .../instrumentation/boto3sqs/__init__.py | 6 +- .../tests/test_boto3sqs_instrumentation.py | 74 +++++++++++++++---- 3 files changed, 66 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 193779659c..2b2ff4c176 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-grpc` AioClientInterceptor should propagate with a Metadata object ([#2363](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2363)) +- `opentelemetry-instrumentation-boto3sqs` Instrument Session and resource + ([#2161](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2161)) ### Added diff --git a/instrumentation/opentelemetry-instrumentation-boto3sqs/src/opentelemetry/instrumentation/boto3sqs/__init__.py b/instrumentation/opentelemetry-instrumentation-boto3sqs/src/opentelemetry/instrumentation/boto3sqs/__init__.py index ee7f4a59a6..c0231f81e4 100644 --- a/instrumentation/opentelemetry-instrumentation-boto3sqs/src/opentelemetry/instrumentation/boto3sqs/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-boto3sqs/src/opentelemetry/instrumentation/boto3sqs/__init__.py @@ -31,7 +31,7 @@ import logging from typing import Any, Collection, Dict, Generator, List, Mapping, Optional -import boto3 +import boto3.session import botocore.client from wrapt import wrap_function_wrapper @@ -382,7 +382,7 @@ def client_wrapper(wrapped, instance, args, kwargs): self._decorate_sqs(type(retval)) return retval - wrap_function_wrapper(boto3, "client", client_wrapper) + wrap_function_wrapper(boto3.session.Session, "client", client_wrapper) def _decorate_sqs(self, sqs_class: type) -> None: """ @@ -433,7 +433,7 @@ def _instrument(self, **kwargs: Dict[str, Any]) -> None: self._decorate_sqs(client_cls) def _uninstrument(self, **kwargs: Dict[str, Any]) -> None: - unwrap(boto3, "client") + unwrap(boto3.session.Session, "client") for client_cls in botocore.client.BaseClient.__subclasses__(): self._un_decorate_sqs(client_cls) diff --git a/instrumentation/opentelemetry-instrumentation-boto3sqs/tests/test_boto3sqs_instrumentation.py b/instrumentation/opentelemetry-instrumentation-boto3sqs/tests/test_boto3sqs_instrumentation.py index a6ca0e062b..7f7f00cf0a 100644 --- a/instrumentation/opentelemetry-instrumentation-boto3sqs/tests/test_boto3sqs_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-boto3sqs/tests/test_boto3sqs_instrumentation.py @@ -20,7 +20,7 @@ import boto3 from botocore.awsrequest import AWSResponse -from wrapt import BoundFunctionWrapper, FunctionWrapper +from wrapt import BoundFunctionWrapper from opentelemetry.instrumentation.boto3sqs import ( Boto3SQSGetter, @@ -37,8 +37,17 @@ from opentelemetry.trace.span import Span, format_span_id, format_trace_id -def _make_sqs_client(): - return boto3.client( +def _make_sqs_client(*, session=False): + return (boto3.Session() if session else boto3).client( + "sqs", + region_name="us-east-1", + aws_access_key_id="dummy", + aws_secret_access_key="dummy", + ) + + +def _make_sqs_resource(*, session=False): + return (boto3.Session() if session else boto3).resource( "sqs", region_name="us-east-1", aws_access_key_id="dummy", @@ -48,7 +57,6 @@ def _make_sqs_client(): class TestBoto3SQSInstrumentor(TestCase): def _assert_instrumented(self, client): - self.assertIsInstance(boto3.client, FunctionWrapper) self.assertIsInstance(client.send_message, BoundFunctionWrapper) self.assertIsInstance(client.send_message_batch, BoundFunctionWrapper) self.assertIsInstance(client.receive_message, BoundFunctionWrapper) @@ -57,6 +65,17 @@ def _assert_instrumented(self, client): client.delete_message_batch, BoundFunctionWrapper ) + def _assert_uninstrumented(self, client): + self.assertNotIsInstance(client.send_message, BoundFunctionWrapper) + self.assertNotIsInstance( + client.send_message_batch, BoundFunctionWrapper + ) + self.assertNotIsInstance(client.receive_message, BoundFunctionWrapper) + self.assertNotIsInstance(client.delete_message, BoundFunctionWrapper) + self.assertNotIsInstance( + client.delete_message_batch, BoundFunctionWrapper + ) + @staticmethod @contextmanager def _active_instrumentor(): @@ -67,19 +86,48 @@ def _active_instrumentor(): Boto3SQSInstrumentor().uninstrument() def test_instrument_api_before_client_init(self) -> None: - with self._active_instrumentor(): - client = _make_sqs_client() - self._assert_instrumented(client) + for session in (False, True): + with self._active_instrumentor(): + client = _make_sqs_client(session=session) + self._assert_instrumented(client) + self._assert_uninstrumented(client) def test_instrument_api_after_client_init(self) -> None: - client = _make_sqs_client() - with self._active_instrumentor(): - self._assert_instrumented(client) + for session in (False, True): + client = _make_sqs_client(session=session) + with self._active_instrumentor(): + self._assert_instrumented(client) + self._assert_uninstrumented(client) def test_instrument_multiple_clients(self): - with self._active_instrumentor(): - self._assert_instrumented(_make_sqs_client()) - self._assert_instrumented(_make_sqs_client()) + for session in (False, True): + with self._active_instrumentor(): + self._assert_instrumented(_make_sqs_client(session=session)) + self._assert_instrumented(_make_sqs_client(session=session)) + + def test_instrument_api_before_resource_init(self) -> None: + for session in (False, True): + with self._active_instrumentor(): + sqs = _make_sqs_resource(session=session) + self._assert_instrumented(sqs.meta.client) + self._assert_uninstrumented(sqs.meta.client) + + def test_instrument_api_after_resource_init(self) -> None: + for session in (False, True): + sqs = _make_sqs_resource(session=session) + with self._active_instrumentor(): + self._assert_instrumented(sqs.meta.client) + self._assert_uninstrumented(sqs.meta.client) + + def test_instrument_multiple_resources(self): + for session in (False, True): + with self._active_instrumentor(): + self._assert_instrumented( + _make_sqs_resource(session=session).meta.client + ) + self._assert_instrumented( + _make_sqs_resource(session=session).meta.client + ) class TestBoto3SQSGetter(TestCase): From 5cb4dab36499985ccf79d30b19dcdfc286d8c261 Mon Sep 17 00:00:00 2001 From: Nick Nicolini <106278816+nicknicolini@users.noreply.github.com> Date: Mon, 22 Apr 2024 17:04:20 -0600 Subject: [PATCH 19/58] Use sqlalchemy version in sqlalchemy commenter instead of opentelemetry library version (#2404) * Use sqlalchemy version in sqlalchemy commenter instead of opentelemetry library version The SqlAlchemy commenter is currently using the library version in the comment it emits, e.g.: ``` { 'db_driver': 'psycopg2', 'db_framework': 'sqlalchemy:0.45b0' } ``` We should instead be using the sqlalchemy version here, as SqlCommenter did before * Fix lint * Fix lint * Add CHANGELOG entry --------- Co-authored-by: Diego Hurtado --- CHANGELOG.md | 3 +++ .../src/opentelemetry/instrumentation/sqlalchemy/engine.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b2ff4c176..ed7658fe87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Use sqlalchemy version in sqlalchemy commenter instead of opentelemetry library version + ([#2404](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2404)) + ### Added - `opentelemetry-instrumentation-pika` Instrumentation for `channel.consume()` (supported diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py index 0632d71faf..a810aedc38 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py @@ -15,13 +15,13 @@ import re import weakref +import sqlalchemy from sqlalchemy.event import ( # pylint: disable=no-name-in-module listen, remove, ) from opentelemetry import trace -from opentelemetry.instrumentation.sqlalchemy.version import __version__ from opentelemetry.instrumentation.sqlcommenter_utils import _add_sql_comment from opentelemetry.instrumentation.utils import _get_opentelemetry_values from opentelemetry.semconv.trace import NetTransportValues, SpanAttributes @@ -227,7 +227,7 @@ def _before_cur_exec( commenter_data = { "db_driver": conn.engine.driver, # Driver/framework centric information. - "db_framework": f"sqlalchemy:{__version__}", + "db_framework": f"sqlalchemy:{sqlalchemy.__version__}", } if self.commenter_options.get("opentelemetry_values", True): From 0980486f2f6bb4cbb3a7711f9530826810a7d27a Mon Sep 17 00:00:00 2001 From: Prakhar Birla Date: Tue, 23 Apr 2024 05:23:59 +0530 Subject: [PATCH 20/58] Fix compatibility issue aio-pika instrumentation (#2450) * - fixed compatibility issue in set_channel of span_builder.py - changed test-requirements-2.txt to use aio-pika==9.0.5 - added test-requirements-3.txt to use aio-pika==9.4.1 (latest atm) - tox.ini: fixed incorrect commands_pre which would always install test-requirements-2.txt and cause dep installation conflict - tox.ini: added aio-pika-3 commands * Add version comment for aio-pika --------- Co-authored-by: Diego Hurtado --- .../instrumentation/aio_pika/span_builder.py | 11 ++++++--- .../test-requirements-2.txt | 6 ++--- .../test-requirements-3.txt | 23 +++++++++++++++++++ tox.ini | 14 +++++------ 4 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-3.txt diff --git a/instrumentation/opentelemetry-instrumentation-aio-pika/src/opentelemetry/instrumentation/aio_pika/span_builder.py b/instrumentation/opentelemetry-instrumentation-aio-pika/src/opentelemetry/instrumentation/aio_pika/span_builder.py index b73afa62b3..c62b1ea9bf 100644 --- a/instrumentation/opentelemetry-instrumentation-aio-pika/src/opentelemetry/instrumentation/aio_pika/span_builder.py +++ b/instrumentation/opentelemetry-instrumentation-aio-pika/src/opentelemetry/instrumentation/aio_pika/span_builder.py @@ -47,8 +47,13 @@ def set_destination(self, destination: str): self._attributes[SpanAttributes.MESSAGING_DESTINATION] = destination def set_channel(self, channel: AbstractChannel): - connection = channel.connection - if getattr(connection, "connection", None): + if hasattr(channel, "_connection"): + # aio_rmq 9.1 and above removed the connection attribute from the abstract listings + connection = channel._connection + else: + # aio_rmq 9.0.5 and below + connection = channel.connection + if hasattr(connection, "connection"): # aio_rmq 7 url = connection.connection.url else: @@ -57,7 +62,7 @@ def set_channel(self, channel: AbstractChannel): self._attributes.update( { SpanAttributes.NET_PEER_NAME: url.host, - SpanAttributes.NET_PEER_PORT: url.port, + SpanAttributes.NET_PEER_PORT: url.port or 5672, } ) diff --git a/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-2.txt b/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-2.txt index 3250c93947..7e1aa15fa0 100644 --- a/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-2.txt +++ b/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-2.txt @@ -1,5 +1,5 @@ -aio-pika==9.4.0 -aiormq==6.8.0 +aio-pika==9.0.5 +aiormq==6.7.1 asgiref==3.7.2 attrs==23.2.0 Deprecated==1.2.14 @@ -8,7 +8,7 @@ importlib-metadata==6.11.0 iniconfig==2.0.0 multidict==6.0.5 packaging==23.2 -pamqp==3.3.0 +pamqp==3.2.1 pluggy==1.4.0 py==1.11.0 py-cpuinfo==9.0.0 diff --git a/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-3.txt b/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-3.txt new file mode 100644 index 0000000000..65c2ff8f0c --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-3.txt @@ -0,0 +1,23 @@ +aio-pika==9.4.1 +aiormq==6.8.0 +asgiref==3.7.2 +attrs==23.2.0 +Deprecated==1.2.14 +idna==3.6 +importlib-metadata==6.11.0 +iniconfig==2.0.0 +multidict==6.0.5 +packaging==23.2 +pamqp==3.3.0 +pluggy==1.4.0 +py==1.11.0 +py-cpuinfo==9.0.0 +pytest==7.1.3 +pytest-benchmark==4.0.0 +tomli==2.0.1 +typing_extensions==4.9.0 +wrapt==1.16.0 +yarl==1.9.4 +zipp==3.17.0 +-e opentelemetry-instrumentation +-e instrumentation/opentelemetry-instrumentation-aio-pika diff --git a/tox.ini b/tox.ini index e48cc7bfe2..c9d7772601 100644 --- a/tox.ini +++ b/tox.ini @@ -265,11 +265,12 @@ envlist = ; opentelemetry-instrumentation-aio-pika ; The numbers at the end of the environment names ; below mean these dependencies are being used: - ; 0: aio_pika~=7.2.0 - ; 1: aio_pika>=8.0.0,<9.0.0 - ; 2: aio_pika>=9.0.0,<10.0.0 - py3{8,9,10,11}-test-instrumentation-aio-pika-{0,1,2} - pypy3-test-instrumentation-aio-pika-{0,1,2} + ; 0: aio_pika==7.2.0 + ; 1: aio_pika==8.3.0 + ; 2: aio_pika==9.0.5 + ; 3: aio_pika==9.4.1 + py3{8,9,10,11}-test-instrumentation-aio-pika-{0,1,2,3} + pypy3-test-instrumentation-aio-pika-{0,1,2,3} ; opentelemetry-instrumentation-kafka-python py3{8,9,10,11}-test-instrumentation-kafka-python @@ -338,6 +339,7 @@ commands_pre = aio-pika-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-0.txt aio-pika-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-1.txt aio-pika-2: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-2.txt + aio-pika-3: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-3.txt kafka-python: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-kafka-python/test-requirements.txt @@ -428,8 +430,6 @@ commands_pre = logging: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt - aio-pika-{0,1,2}: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-2.txt - aiohttp-client: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-client/test-requirements.txt aiohttp-server: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-server/test-requirements.txt From 1cd8ccb28a1823ab7dfc81d21c5e5c71f9f0eb7b Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 23 Apr 2024 17:15:22 +0200 Subject: [PATCH 21/58] CHANGELOG: deduplicate unreleased entries (#2451) --- CHANGELOG.md | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed7658fe87..4cbc913346 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,16 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- Use sqlalchemy version in sqlalchemy commenter instead of opentelemetry library version - ([#2404](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2404)) - -### Added - -- `opentelemetry-instrumentation-pika` Instrumentation for `channel.consume()` (supported - only for global, non channel specific instrumentation) - ([#2397](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2397))) - - ### Breaking changes - Rename `type` attribute to `asgi.event.type` in `opentelemetry-instrumentation-asgi` @@ -35,6 +25,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2425](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2425)) - `opentelemetry-instrumentation-threading` Initial release for threading ([#2253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2253)) +- `opentelemetry-instrumentation-pika` Instrumentation for `channel.consume()` (supported + only for global, non channel specific instrumentation) + ([#2397](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2397))) ### Fixed @@ -42,15 +35,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2363](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2363)) - `opentelemetry-instrumentation-boto3sqs` Instrument Session and resource ([#2161](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2161)) - -### Added - -- `opentelemetry-sdk-extension-aws` Register AWS resource detectors under the `opentelemetry_resource_detector` entry point - ([#2382](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2382)) -- `opentelemetry-instrumentation-wsgi` Implement new semantic convention opt-in with stable http semantic conventions - ([#2425](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2425)) -- `opentelemetry-instrumentation-threading` Initial release for threading - ([#2253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2253)) +- Use sqlalchemy version in sqlalchemy commenter instead of opentelemetry library version + ([#2404](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2404)) ## Version 1.24.0/0.45b0 (2024-03-28) From a0c3211c4fd3326279740480c0c1938a17a125c8 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 23 Apr 2024 17:45:48 +0200 Subject: [PATCH 22/58] Bump black to 24.3.0 (#2452) --- .flake8 | 4 +++ dev-requirements.txt | 2 +- .../prometheus_remote_write/gen/remote_pb2.py | 12 +++---- .../prometheus_remote_write/gen/types_pb2.py | 36 +++++++++---------- .../instrumentation/asgi/__init__.py | 6 ++-- .../instrumentation/asyncpg/__init__.py | 12 +++---- .../botocore/extensions/lmbd.py | 6 ++-- .../botocore/extensions/sns.py | 6 ++-- .../botocore/extensions/sqs.py | 6 ++-- .../django/middleware/otel_middleware.py | 18 +++++----- .../middleware/sqlcommenter_middleware.py | 24 ++++++++----- .../tests/test_middleware.py | 8 +++-- .../tests/views.py | 12 +++---- .../instrumentation/elasticsearch/__init__.py | 6 ++-- .../instrumentation/falcon/__init__.py | 6 ++-- .../instrumentation/flask/__init__.py | 12 +++---- .../tests/base_test.py | 18 +++++----- .../tests/protobuf/test_server_pb2.py | 4 +-- .../instrumentation/httpx/__init__.py | 4 +-- .../instrumentation/pika/pika_instrumentor.py | 6 ++-- .../instrumentation/pika/utils.py | 17 ++++----- .../instrumentation/pymemcache/__init__.py | 12 +++---- .../instrumentation/pymongo/__init__.py | 3 +- .../instrumentation/pyramid/callbacks.py | 12 +++---- .../instrumentation/redis/util.py | 12 +++---- .../instrumentation/requests/__init__.py | 20 ++++++----- .../instrumentation/sqlalchemy/engine.py | 12 +++---- .../system_metrics/__init__.py | 20 ++++++----- .../instrumentation/tornado/__init__.py | 6 ++-- .../instrumentation/tortoiseorm/__init__.py | 18 +++++----- .../instrumentation/urllib/__init__.py | 14 ++++---- .../instrumentation/urllib3/__init__.py | 8 +++-- .../tests/test_wsgi_middleware.py | 18 +++++----- .../opentelemetry/instrumentation/_semconv.py | 12 +++---- .../propagators/ot_trace/__init__.py | 6 ++-- .../resource/detector/azure/app_service.py | 20 +++++------ scripts/check_for_valid_readme.py | 1 + 37 files changed, 219 insertions(+), 200 deletions(-) diff --git a/.flake8 b/.flake8 index 8b2144c00f..5683cfc1f3 100644 --- a/.flake8 +++ b/.flake8 @@ -12,6 +12,10 @@ ignore = # allow whitespace before ':' (https://github.com/psf/black#slices) E203 + # conflicts with black + E701 + E704 + exclude = .bzr .git diff --git a/dev-requirements.txt b/dev-requirements.txt index fffb4c445d..1c49c57b7e 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,7 +1,7 @@ pylint==3.0.2 flake8==6.1.0 isort==5.12.0 -black==22.3.0 +black==24.3.0 httpretty==1.1.4 mypy==0.931 sphinx==7.1.2 diff --git a/exporter/opentelemetry-exporter-prometheus-remote-write/src/opentelemetry/exporter/prometheus_remote_write/gen/remote_pb2.py b/exporter/opentelemetry-exporter-prometheus-remote-write/src/opentelemetry/exporter/prometheus_remote_write/gen/remote_pb2.py index 3efcb36536..f8724074f6 100644 --- a/exporter/opentelemetry-exporter-prometheus-remote-write/src/opentelemetry/exporter/prometheus_remote_write/gen/remote_pb2.py +++ b/exporter/opentelemetry-exporter-prometheus-remote-write/src/opentelemetry/exporter/prometheus_remote_write/gen/remote_pb2.py @@ -34,13 +34,13 @@ DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b"Z\006prompb" _WRITEREQUEST.fields_by_name["timeseries"]._options = None - _WRITEREQUEST.fields_by_name[ - "timeseries" - ]._serialized_options = b"\310\336\037\000" + _WRITEREQUEST.fields_by_name["timeseries"]._serialized_options = ( + b"\310\336\037\000" + ) _WRITEREQUEST.fields_by_name["metadata"]._options = None - _WRITEREQUEST.fields_by_name[ - "metadata" - ]._serialized_options = b"\310\336\037\000" + _WRITEREQUEST.fields_by_name["metadata"]._serialized_options = ( + b"\310\336\037\000" + ) _WRITEREQUEST._serialized_start = 216 _WRITEREQUEST._serialized_end = 338 _READREQUEST._serialized_start = 341 diff --git a/exporter/opentelemetry-exporter-prometheus-remote-write/src/opentelemetry/exporter/prometheus_remote_write/gen/types_pb2.py b/exporter/opentelemetry-exporter-prometheus-remote-write/src/opentelemetry/exporter/prometheus_remote_write/gen/types_pb2.py index fbfa2123ad..30cf2e38cc 100644 --- a/exporter/opentelemetry-exporter-prometheus-remote-write/src/opentelemetry/exporter/prometheus_remote_write/gen/types_pb2.py +++ b/exporter/opentelemetry-exporter-prometheus-remote-write/src/opentelemetry/exporter/prometheus_remote_write/gen/types_pb2.py @@ -31,31 +31,31 @@ DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b"Z\006prompb" _EXEMPLAR.fields_by_name["labels"]._options = None - _EXEMPLAR.fields_by_name[ - "labels" - ]._serialized_options = b"\310\336\037\000" + _EXEMPLAR.fields_by_name["labels"]._serialized_options = ( + b"\310\336\037\000" + ) _TIMESERIES.fields_by_name["labels"]._options = None - _TIMESERIES.fields_by_name[ - "labels" - ]._serialized_options = b"\310\336\037\000" + _TIMESERIES.fields_by_name["labels"]._serialized_options = ( + b"\310\336\037\000" + ) _TIMESERIES.fields_by_name["samples"]._options = None - _TIMESERIES.fields_by_name[ - "samples" - ]._serialized_options = b"\310\336\037\000" + _TIMESERIES.fields_by_name["samples"]._serialized_options = ( + b"\310\336\037\000" + ) _TIMESERIES.fields_by_name["exemplars"]._options = None - _TIMESERIES.fields_by_name[ - "exemplars" - ]._serialized_options = b"\310\336\037\000" + _TIMESERIES.fields_by_name["exemplars"]._serialized_options = ( + b"\310\336\037\000" + ) _LABELS.fields_by_name["labels"]._options = None _LABELS.fields_by_name["labels"]._serialized_options = b"\310\336\037\000" _CHUNKEDSERIES.fields_by_name["labels"]._options = None - _CHUNKEDSERIES.fields_by_name[ - "labels" - ]._serialized_options = b"\310\336\037\000" + _CHUNKEDSERIES.fields_by_name["labels"]._serialized_options = ( + b"\310\336\037\000" + ) _CHUNKEDSERIES.fields_by_name["chunks"]._options = None - _CHUNKEDSERIES.fields_by_name[ - "chunks" - ]._serialized_options = b"\310\336\037\000" + _CHUNKEDSERIES.fields_by_name["chunks"]._serialized_options = ( + b"\310\336\037\000" + ) _METRICMETADATA._serialized_start = 152 _METRICMETADATA._serialized_end = 400 _METRICMETADATA_METRICTYPE._serialized_start = 279 diff --git a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py index 8e4d699cbb..405c470ceb 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py @@ -695,9 +695,9 @@ async def otel_send(message: dict[str, Any]): if send_span.is_recording(): if message["type"] == "http.response.start": status_code = message["status"] - duration_attrs[ - SpanAttributes.HTTP_STATUS_CODE - ] = status_code + duration_attrs[SpanAttributes.HTTP_STATUS_CODE] = ( + status_code + ) set_status_code(server_span, status_code) set_status_code(send_span, status_code) diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/__init__.py b/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/__init__.py index 11c579f96a..798a5dc00b 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/__init__.py @@ -77,14 +77,14 @@ def _hydrate_span_from_args(connection, query, parameters) -> dict: if isinstance(addr, tuple): span_attributes[SpanAttributes.NET_PEER_NAME] = addr[0] span_attributes[SpanAttributes.NET_PEER_PORT] = addr[1] - span_attributes[ - SpanAttributes.NET_TRANSPORT - ] = NetTransportValues.IP_TCP.value + span_attributes[SpanAttributes.NET_TRANSPORT] = ( + NetTransportValues.IP_TCP.value + ) elif isinstance(addr, str): span_attributes[SpanAttributes.NET_PEER_NAME] = addr - span_attributes[ - SpanAttributes.NET_TRANSPORT - ] = NetTransportValues.OTHER.value + span_attributes[SpanAttributes.NET_TRANSPORT] = ( + NetTransportValues.OTHER.value + ) if query is not None: span_attributes[SpanAttributes.DB_STATEMENT] = query diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/lmbd.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/lmbd.py index 299a37ab6c..57fb8b6794 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/lmbd.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/lmbd.py @@ -62,9 +62,9 @@ def extract_attributes( cls, call_context: _AwsSdkCallContext, attributes: _AttributeMapT ): attributes[SpanAttributes.FAAS_INVOKED_PROVIDER] = "aws" - attributes[ - SpanAttributes.FAAS_INVOKED_NAME - ] = cls._parse_function_name(call_context) + attributes[SpanAttributes.FAAS_INVOKED_NAME] = ( + cls._parse_function_name(call_context) + ) attributes[SpanAttributes.FAAS_INVOKED_REGION] = call_context.region @classmethod diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/sns.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/sns.py index aa55ae697f..9c3df3a2bc 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/sns.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/sns.py @@ -76,9 +76,9 @@ def extract_attributes( destination_name, is_phone_number = cls._extract_destination_name( call_context ) - attributes[ - SpanAttributes.MESSAGING_DESTINATION_KIND - ] = MessagingDestinationKindValues.TOPIC.value + attributes[SpanAttributes.MESSAGING_DESTINATION_KIND] = ( + MessagingDestinationKindValues.TOPIC.value + ) attributes[SpanAttributes.MESSAGING_DESTINATION] = destination_name # TODO: Use SpanAttributes.MESSAGING_DESTINATION_NAME when opentelemetry-semantic-conventions 0.42b0 is released diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/sqs.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/sqs.py index 777108cbb5..194e47b57f 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/sqs.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/sqs.py @@ -35,9 +35,9 @@ def extract_attributes(self, attributes: _AttributeMapT): attributes[SpanAttributes.MESSAGING_SYSTEM] = "aws.sqs" attributes[SpanAttributes.MESSAGING_URL] = queue_url try: - attributes[ - SpanAttributes.MESSAGING_DESTINATION - ] = queue_url.split("/")[-1] + attributes[SpanAttributes.MESSAGING_DESTINATION] = ( + queue_url.split("/")[-1] + ) except IndexError: _logger.error( "Could not extract messaging destination from '%s'", diff --git a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/otel_middleware.py b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/otel_middleware.py index bc677a81cf..1b747fd2c0 100644 --- a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/otel_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/otel_middleware.py @@ -163,9 +163,9 @@ class _DjangoMiddleware(MiddlewareMixin): _active_request_counter = None _otel_request_hook: Callable[[Span, HttpRequest], None] = None - _otel_response_hook: Callable[ - [Span, HttpRequest, HttpResponse], None - ] = None + _otel_response_hook: Callable[[Span, HttpRequest, HttpResponse], None] = ( + None + ) @staticmethod def _get_span_name(request): @@ -229,9 +229,9 @@ def process_request(self, request): ) duration_attrs = _parse_duration_attrs(attributes) - request.META[ - self._environ_active_request_attr_key - ] = active_requests_count_attrs + request.META[self._environ_active_request_attr_key] = ( + active_requests_count_attrs + ) request.META[self._environ_duration_attr_key] = duration_attrs self._active_request_counter.add(1, active_requests_count_attrs) if span.is_recording(): @@ -336,9 +336,9 @@ def process_response(self, request, response): self._environ_duration_attr_key, None ) if duration_attrs: - duration_attrs[ - SpanAttributes.HTTP_STATUS_CODE - ] = response.status_code + duration_attrs[SpanAttributes.HTTP_STATUS_CODE] = ( + response.status_code + ) request_start_time = request.META.pop(self._environ_timer_key, None) if activation and span: diff --git a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/sqlcommenter_middleware.py b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/sqlcommenter_middleware.py index 30492a8be5..ef53d5dc38 100644 --- a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/sqlcommenter_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/sqlcommenter_middleware.py @@ -83,20 +83,26 @@ def __call__(self, execute: Type[T], sql, params, many, context) -> T: sql = _add_sql_comment( sql, # Information about the controller. - controller=resolver_match.view_name - if resolver_match and with_controller - else None, + controller=( + resolver_match.view_name + if resolver_match and with_controller + else None + ), # route is the pattern that matched a request with a controller i.e. the regex # See https://docs.djangoproject.com/en/stable/ref/urlresolvers/#django.urls.ResolverMatch.route # getattr() because the attribute doesn't exist in Django < 2.2. - route=getattr(resolver_match, "route", None) - if resolver_match and with_route - else None, + route=( + getattr(resolver_match, "route", None) + if resolver_match and with_route + else None + ), # app_name is the application namespace for the URL pattern that matches the URL. # See https://docs.djangoproject.com/en/stable/ref/urlresolvers/#django.urls.ResolverMatch.app_name - app_name=(resolver_match.app_name or None) - if resolver_match and with_app_name - else None, + app_name=( + (resolver_match.app_name or None) + if resolver_match and with_app_name + else None + ), # Framework centric information. framework=f"django:{_django_version}" if with_framework else None, # Information about the database and driver. diff --git a/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py b/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py index 4d221fae62..63af1e6b86 100644 --- a/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py @@ -155,9 +155,11 @@ def test_templated_route_get(self): self.assertEqual( span.name, - "GET ^route/(?P[0-9]{4})/template/$" - if DJANGO_2_2 - else "GET", + ( + "GET ^route/(?P[0-9]{4})/template/$" + if DJANGO_2_2 + else "GET" + ), ) self.assertEqual(span.kind, SpanKind.SERVER) self.assertEqual(span.status.status_code, StatusCode.UNSET) diff --git a/instrumentation/opentelemetry-instrumentation-django/tests/views.py b/instrumentation/opentelemetry-instrumentation-django/tests/views.py index 452a7c0fdd..6310664100 100644 --- a/instrumentation/opentelemetry-instrumentation-django/tests/views.py +++ b/instrumentation/opentelemetry-instrumentation-django/tests/views.py @@ -35,12 +35,12 @@ def response_with_custom_header(request): response = HttpResponse() response["custom-test-header-1"] = "test-header-value-1" response["custom-test-header-2"] = "test-header-value-2" - response[ - "my-custom-regex-header-1" - ] = "my-custom-regex-value-1,my-custom-regex-value-2" - response[ - "my-custom-regex-header-2" - ] = "my-custom-regex-value-3,my-custom-regex-value-4" + response["my-custom-regex-header-1"] = ( + "my-custom-regex-value-1,my-custom-regex-value-2" + ) + response["my-custom-regex-header-2"] = ( + "my-custom-regex-value-3,my-custom-regex-value-4" + ) response["my-secret-header"] = "my-secret-value" return response diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py index 0f5056de83..ceb50cac56 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py @@ -247,9 +247,9 @@ def wrapper(wrapped, _, args, kwargs): if body: # Don't set db.statement for bulk requests, as it can be very large if isinstance(body, dict): - attributes[ - SpanAttributes.DB_STATEMENT - ] = sanitize_body(body) + attributes[SpanAttributes.DB_STATEMENT] = ( + sanitize_body(body) + ) if params: attributes["elasticsearch.params"] = str(params) if doc_id: diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index 06a550cf3f..79c9a0cf0f 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -382,9 +382,9 @@ def _start_response(status, response_headers, *args, **kwargs): raise finally: if span.is_recording(): - duration_attrs[ - SpanAttributes.HTTP_STATUS_CODE - ] = span.attributes.get(SpanAttributes.HTTP_STATUS_CODE) + duration_attrs[SpanAttributes.HTTP_STATUS_CODE] = ( + span.attributes.get(SpanAttributes.HTTP_STATUS_CODE) + ) duration = max(round((default_timer() - start) * 1000), 0) self.duration_histogram.record(duration, duration_attrs) self.active_requests_counter.add(-1, active_requests_count_attrs) diff --git a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py index ba1031655e..a17f83a877 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py @@ -334,9 +334,9 @@ def _start_response(status, response_headers, *args, **kwargs): ) status_code = otel_wsgi._parse_status_code(status) if status_code is not None: - duration_attrs[ - SpanAttributes.HTTP_STATUS_CODE - ] = status_code + duration_attrs[SpanAttributes.HTTP_STATUS_CODE] = ( + status_code + ) if ( span.is_recording() and span.kind == trace.SpanKind.SERVER @@ -641,9 +641,9 @@ def instrument_app( tracer, excluded_urls=excluded_urls, enable_commenter=enable_commenter, - commenter_options=commenter_options - if commenter_options - else {}, + commenter_options=( + commenter_options if commenter_options else {} + ), ) app._before_request = _before_request app.before_request(_before_request) diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/base_test.py b/instrumentation/opentelemetry-instrumentation-flask/tests/base_test.py index 3c8073f261..307ac3ccf0 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/base_test.py +++ b/instrumentation/opentelemetry-instrumentation-flask/tests/base_test.py @@ -76,15 +76,15 @@ def _custom_response_headers(): resp = flask.Response("test response") resp.headers["content-type"] = "text/plain; charset=utf-8" resp.headers["content-length"] = "13" - resp.headers[ - "my-custom-header" - ] = "my-custom-value-1,my-custom-header-2" - resp.headers[ - "my-custom-regex-header-1" - ] = "my-custom-regex-value-1,my-custom-regex-value-2" - resp.headers[ - "My-Custom-Regex-Header-2" - ] = "my-custom-regex-value-3,my-custom-regex-value-4" + resp.headers["my-custom-header"] = ( + "my-custom-value-1,my-custom-header-2" + ) + resp.headers["my-custom-regex-header-1"] = ( + "my-custom-regex-value-1,my-custom-regex-value-2" + ) + resp.headers["My-Custom-Regex-Header-2"] = ( + "my-custom-regex-value-3,my-custom-regex-value-4" + ) resp.headers["my-secret-header"] = "my-secret-value" return resp diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/protobuf/test_server_pb2.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/protobuf/test_server_pb2.py index ad3dcf3fe7..f00b5f82e7 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/protobuf/test_server_pb2.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/protobuf/test_server_pb2.py @@ -142,7 +142,7 @@ (_message.Message,), { "DESCRIPTOR": _REQUEST, - "__module__": "test_server_pb2" + "__module__": "test_server_pb2", # @@protoc_insertion_point(class_scope:Request) }, ) @@ -153,7 +153,7 @@ (_message.Message,), { "DESCRIPTOR": _RESPONSE, - "__module__": "test_server_pb2" + "__module__": "test_server_pb2", # @@protoc_insertion_point(class_scope:Response) }, ) diff --git a/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py b/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py index e6609157c4..7fcc7128be 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py @@ -433,9 +433,7 @@ async def __aexit__( ) -> None: await self._transport.__aexit__(exc_type, exc_value, traceback) - async def handle_async_request( - self, *args, **kwargs - ) -> typing.Union[ + async def handle_async_request(self, *args, **kwargs) -> typing.Union[ typing.Tuple[int, "Headers", httpx.AsyncByteStream, dict], httpx.Response, ]: diff --git a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/pika_instrumentor.py b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/pika_instrumentor.py index 76261c89ce..f37f74e396 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/pika_instrumentor.py +++ b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/pika_instrumentor.py @@ -156,9 +156,9 @@ def uninstrument_channel(channel: BlockingChannel) -> None: callback_attr = PikaInstrumentor.CONSUMER_CALLBACK_ATTR consumer_callback = getattr(client_info, callback_attr, None) if hasattr(consumer_callback, "_original_callback"): - channel._consumer_infos[ - consumers_tag - ] = consumer_callback._original_callback + channel._consumer_infos[consumers_tag] = ( + consumer_callback._original_callback + ) PikaInstrumentor._uninstrument_channel_functions(channel) def _decorate_channel_function( diff --git a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/utils.py b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/utils.py index 5afa5d9ee6..2b4d1204ea 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/utils.py +++ b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/utils.py @@ -38,8 +38,7 @@ def keys(self, carrier: CarrierT) -> List[str]: HookT = Callable[[Span, bytes, BasicProperties], None] -def dummy_callback(span: Span, body: bytes, properties: BasicProperties): - ... +def dummy_callback(span: Span, body: bytes, properties: BasicProperties): ... def _decorate_callback( @@ -66,9 +65,9 @@ def decorated_callback( tracer, channel, properties, - destination=method.exchange - if method.exchange - else method.routing_key, + destination=( + method.exchange if method.exchange else method.routing_key + ), span_kind=SpanKind.CONSUMER, task_name=task_name, operation=MessagingOperationValues.RECEIVE, @@ -243,9 +242,11 @@ def popleft(self, *args, **kwargs): self._self_tracer, None, properties, - destination=method.exchange - if method.exchange - else method.routing_key, + destination=( + method.exchange + if method.exchange + else method.routing_key + ), span_kind=SpanKind.CONSUMER, task_name=self._self_queue_consumer_generator.consumer_tag, operation=MessagingOperationValues.RECEIVE, diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/__init__.py b/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/__init__.py index 512ce9ea56..d763734aca 100644 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/__init__.py @@ -162,14 +162,14 @@ def _get_address_attributes(instance): host, port = instance.server address_attributes[SpanAttributes.NET_PEER_NAME] = host address_attributes[SpanAttributes.NET_PEER_PORT] = port - address_attributes[ - SpanAttributes.NET_TRANSPORT - ] = NetTransportValues.IP_TCP.value + address_attributes[SpanAttributes.NET_TRANSPORT] = ( + NetTransportValues.IP_TCP.value + ) elif isinstance(instance.server, str): address_attributes[SpanAttributes.NET_PEER_NAME] = instance.server - address_attributes[ - SpanAttributes.NET_TRANSPORT - ] = NetTransportValues.OTHER.value + address_attributes[SpanAttributes.NET_TRANSPORT] = ( + NetTransportValues.OTHER.value + ) return address_attributes diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py b/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py index 506669a5c6..f55aa2be33 100644 --- a/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py @@ -98,8 +98,7 @@ def failed_hook(span, event): FailedHookT = Callable[[Span, monitoring.CommandFailedEvent], None] -def dummy_callback(span, event): - ... +def dummy_callback(span, event): ... class CommandTracer(monitoring.CommandListener): diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py b/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py index 4f17da3da5..ede3e09608 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py +++ b/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py @@ -106,9 +106,9 @@ def _before_traversal(event): if span.is_recording(): attributes = otel_wsgi.collect_request_attributes(request_environ) if request.matched_route: - attributes[ - SpanAttributes.HTTP_ROUTE - ] = request.matched_route.pattern + attributes[SpanAttributes.HTTP_ROUTE] = ( + request.matched_route.pattern + ) for key, value in attributes.items(): span.set_attribute(key, value) if span.kind == trace.SpanKind.SERVER: @@ -201,9 +201,9 @@ def trace_tween(request): status = getattr(response, "status", status) status_code = otel_wsgi._parse_status_code(status) if status_code is not None: - duration_attrs[ - SpanAttributes.HTTP_STATUS_CODE - ] = otel_wsgi._parse_status_code(status) + duration_attrs[SpanAttributes.HTTP_STATUS_CODE] = ( + otel_wsgi._parse_status_code(status) + ) duration_histogram.record(duration, duration_attrs) active_requests_counter.add(-1, active_requests_count_attrs) span = request.environ.get(_ENVIRON_SPAN_KEY) diff --git a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py index 3c274c8c43..2a24ead79a 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py +++ b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py @@ -36,14 +36,14 @@ def _extract_conn_attributes(conn_kwargs): attributes[SpanAttributes.NET_PEER_PORT] = conn_kwargs.get( "port", 6379 ) - attributes[ - SpanAttributes.NET_TRANSPORT - ] = NetTransportValues.IP_TCP.value + attributes[SpanAttributes.NET_TRANSPORT] = ( + NetTransportValues.IP_TCP.value + ) except KeyError: attributes[SpanAttributes.NET_PEER_NAME] = conn_kwargs.get("path", "") - attributes[ - SpanAttributes.NET_TRANSPORT - ] = NetTransportValues.OTHER.value + attributes[SpanAttributes.NET_TRANSPORT] = ( + NetTransportValues.OTHER.value + ) return attributes diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index 2052fe47cd..f468ff87ff 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -189,9 +189,9 @@ def get_or_create_headers(): sem_conv_opt_in_mode, ) # Use semconv library when available - span_attributes[ - _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS - ] = parsed_url.hostname + span_attributes[_SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS] = ( + parsed_url.hostname + ) if parsed_url.port: _set_http_peer_port_client( metric_labels, parsed_url.port, sem_conv_opt_in_mode @@ -201,9 +201,9 @@ def get_or_create_headers(): span_attributes, parsed_url.port, sem_conv_opt_in_mode ) # Use semconv library when available - span_attributes[ - _SPAN_ATTRIBUTES_NETWORK_PEER_PORT - ] = parsed_url.port + span_attributes[_SPAN_ATTRIBUTES_NETWORK_PEER_PORT] = ( + parsed_url.port + ) except ValueError: pass @@ -413,9 +413,11 @@ def _instrument(self, **kwargs): duration_histogram_new, request_hook=kwargs.get("request_hook"), response_hook=kwargs.get("response_hook"), - excluded_urls=_excluded_urls_from_env - if excluded_urls is None - else parse_excluded_urls(excluded_urls), + excluded_urls=( + _excluded_urls_from_env + if excluded_urls is None + else parse_excluded_urls(excluded_urls) + ), sem_conv_opt_in_mode=semconv_opt_in_mode, ) diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py index a810aedc38..172c1193f3 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py @@ -296,18 +296,18 @@ def _get_attributes_from_cursor(vendor, cursor, attrs): is_unix_socket = info.host and info.host.startswith("/") if is_unix_socket: - attrs[ - SpanAttributes.NET_TRANSPORT - ] = NetTransportValues.OTHER.value + attrs[SpanAttributes.NET_TRANSPORT] = ( + NetTransportValues.OTHER.value + ) if info.port: # postgresql enforces this pattern on all socket names attrs[SpanAttributes.NET_PEER_NAME] = os.path.join( info.host, f".s.PGSQL.{info.port}" ) else: - attrs[ - SpanAttributes.NET_TRANSPORT - ] = NetTransportValues.IP_TCP.value + attrs[SpanAttributes.NET_TRANSPORT] = ( + NetTransportValues.IP_TCP.value + ) attrs[SpanAttributes.NET_PEER_NAME] = info.host if info.port: attrs[SpanAttributes.NET_PEER_PORT] = int(info.port) 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 32766fa0c5..74d4f6a431 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 @@ -481,9 +481,11 @@ def _get_system_swap_utilization( if hasattr(system_swap, metric): self._system_swap_utilization_labels["state"] = metric yield Observation( - getattr(system_swap, metric) / system_swap.total - if system_swap.total - else 0, + ( + getattr(system_swap, metric) / system_swap.total + if system_swap.total + else 0 + ), self._system_swap_utilization_labels.copy(), ) @@ -556,9 +558,9 @@ def _get_system_network_dropped_packets( for metric in self._config["system.network.dropped.packets"]: in_out = {"receive": "in", "transmit": "out"}[metric] if hasattr(counters, f"drop{in_out}"): - self._system_network_dropped_packets_labels[ - "device" - ] = device + self._system_network_dropped_packets_labels["device"] = ( + device + ) self._system_network_dropped_packets_labels[ "direction" ] = metric @@ -629,9 +631,9 @@ def _get_system_network_connections( 1: "tcp", 2: "udp", }[net_connection.type.value] - self._system_network_connections_labels[ - "state" - ] = net_connection.status + self._system_network_connections_labels["state"] = ( + net_connection.status + ) self._system_network_connections_labels[metric] = getattr( net_connection, metric ) diff --git a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py index 5a39538837..5c99457a39 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py @@ -455,9 +455,9 @@ def _get_attributes_from_request(request): if hasattr(request.connection, "context") and getattr( request.connection.context, "_orig_remote_ip", None ): - attrs[ - SpanAttributes.NET_PEER_IP - ] = request.connection.context._orig_remote_ip + attrs[SpanAttributes.NET_PEER_IP] = ( + request.connection.context._orig_remote_ip + ) return extract_attributes_from_object( request, _traced_request_attrs, attrs diff --git a/instrumentation/opentelemetry-instrumentation-tortoiseorm/src/opentelemetry/instrumentation/tortoiseorm/__init__.py b/instrumentation/opentelemetry-instrumentation-tortoiseorm/src/opentelemetry/instrumentation/tortoiseorm/__init__.py index 7988daf130..cebcb81ced 100644 --- a/instrumentation/opentelemetry-instrumentation-tortoiseorm/src/opentelemetry/instrumentation/tortoiseorm/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-tortoiseorm/src/opentelemetry/instrumentation/tortoiseorm/__init__.py @@ -229,17 +229,17 @@ def _hydrate_span_from_args(self, connection, query, parameters) -> dict: capabilities = getattr(connection, "capabilities", None) if capabilities is not None: if capabilities.dialect == "sqlite": - span_attributes[ - SpanAttributes.DB_SYSTEM - ] = DbSystemValues.SQLITE.value + span_attributes[SpanAttributes.DB_SYSTEM] = ( + DbSystemValues.SQLITE.value + ) elif capabilities.dialect == "postgres": - span_attributes[ - SpanAttributes.DB_SYSTEM - ] = DbSystemValues.POSTGRESQL.value + span_attributes[SpanAttributes.DB_SYSTEM] = ( + DbSystemValues.POSTGRESQL.value + ) elif capabilities.dialect == "mysql": - span_attributes[ - SpanAttributes.DB_SYSTEM - ] = DbSystemValues.MYSQL.value + span_attributes[SpanAttributes.DB_SYSTEM] = ( + DbSystemValues.MYSQL.value + ) dbname = getattr(connection, "filename", None) if dbname: span_attributes[SpanAttributes.DB_NAME] = dbname diff --git a/instrumentation/opentelemetry-instrumentation-urllib/src/opentelemetry/instrumentation/urllib/__init__.py b/instrumentation/opentelemetry-instrumentation-urllib/src/opentelemetry/instrumentation/urllib/__init__.py index 3738c4d2c6..befc022b35 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/src/opentelemetry/instrumentation/urllib/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-urllib/src/opentelemetry/instrumentation/urllib/__init__.py @@ -156,9 +156,11 @@ def _instrument(self, **kwargs): histograms, request_hook=kwargs.get("request_hook"), response_hook=kwargs.get("response_hook"), - excluded_urls=_excluded_urls_from_env - if excluded_urls is None - else parse_excluded_urls(excluded_urls), + excluded_urls=( + _excluded_urls_from_env + if excluded_urls is None + else parse_excluded_urls(excluded_urls) + ), ) def _uninstrument(self, **kwargs): @@ -251,9 +253,9 @@ def _instrumented_open_call( ver_ = str(getattr(result, "version", "")) if ver_: - labels[ - SpanAttributes.HTTP_FLAVOR - ] = f"{ver_[:1]}.{ver_[:-1]}" + labels[SpanAttributes.HTTP_FLAVOR] = ( + f"{ver_[:1]}.{ver_[:-1]}" + ) _record_histograms( histograms, labels, request, result, elapsed_time diff --git a/instrumentation/opentelemetry-instrumentation-urllib3/src/opentelemetry/instrumentation/urllib3/__init__.py b/instrumentation/opentelemetry-instrumentation-urllib3/src/opentelemetry/instrumentation/urllib3/__init__.py index 985f291199..add5db8f19 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib3/src/opentelemetry/instrumentation/urllib3/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-urllib3/src/opentelemetry/instrumentation/urllib3/__init__.py @@ -200,9 +200,11 @@ def _instrument(self, **kwargs): request_hook=kwargs.get("request_hook"), response_hook=kwargs.get("response_hook"), url_filter=kwargs.get("url_filter"), - excluded_urls=_excluded_urls_from_env - if excluded_urls is None - else parse_excluded_urls(excluded_urls), + excluded_urls=( + _excluded_urls_from_env + if excluded_urls is None + else parse_excluded_urls(excluded_urls) + ), ) def _uninstrument(self, **kwargs): diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index 985fbe0571..b55ac6808f 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -252,9 +252,9 @@ def validate_response( if old_sem_conv: expected_attributes[SpanAttributes.HTTP_METHOD] = http_method if new_sem_conv: - expected_attributes[ - SpanAttributes.HTTP_REQUEST_METHOD - ] = http_method + expected_attributes[SpanAttributes.HTTP_REQUEST_METHOD] = ( + http_method + ) self.assertEqual(span_list[0].attributes, expected_attributes) def test_basic_wsgi_call(self): @@ -562,9 +562,9 @@ def validate_url( parts.path, 1 )[1] if parts.query: - expected_new[ - SpanAttributes.URL_QUERY - ] = expected_url.split(parts.query, 1)[1] + expected_new[SpanAttributes.URL_QUERY] = ( + expected_url.split(parts.query, 1)[1] + ) else: expected_new[SpanAttributes.HTTP_URL] = expected_url if has_host: @@ -710,9 +710,9 @@ def test_request_attributes_pathless(self): def test_request_attributes_with_full_request_uri(self): self.environ["HTTP_HOST"] = "127.0.0.1:8080" self.environ["REQUEST_METHOD"] = "CONNECT" - self.environ[ - "REQUEST_URI" - ] = "http://docs.python.org:80/3/library/urllib.parse.html?highlight=params#url-parsing" # Might happen in a CONNECT request + self.environ["REQUEST_URI"] = ( + "http://docs.python.org:80/3/library/urllib.parse.html?highlight=params#url-parsing" # Might happen in a CONNECT request + ) expected_old = { SpanAttributes.HTTP_HOST: "127.0.0.1:8080", SpanAttributes.HTTP_TARGET: "http://docs.python.org:80/3/library/urllib.parse.html?highlight=params#url-parsing", diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index 31c2486acc..8f236b5479 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -368,16 +368,16 @@ def _set_status( span.set_attribute( SpanAttributes.HTTP_RESPONSE_STATUS_CODE, status_code ) - metrics_attributes[ - SpanAttributes.HTTP_RESPONSE_STATUS_CODE - ] = status_code + metrics_attributes[SpanAttributes.HTTP_RESPONSE_STATUS_CODE] = ( + status_code + ) if status == StatusCode.ERROR: span.set_attribute( _SPAN_ATTRIBUTES_ERROR_TYPE, status_code_str ) - metrics_attributes[ - _SPAN_ATTRIBUTES_ERROR_TYPE - ] = status_code_str + metrics_attributes[_SPAN_ATTRIBUTES_ERROR_TYPE] = ( + status_code_str + ) span.set_status(Status(status)) diff --git a/propagator/opentelemetry-propagator-ot-trace/src/opentelemetry/propagators/ot_trace/__init__.py b/propagator/opentelemetry-propagator-ot-trace/src/opentelemetry/propagators/ot_trace/__init__.py index c5c3496248..7924fe0b57 100644 --- a/propagator/opentelemetry-propagator-ot-trace/src/opentelemetry/propagators/ot_trace/__init__.py +++ b/propagator/opentelemetry-propagator-ot-trace/src/opentelemetry/propagators/ot_trace/__init__.py @@ -98,9 +98,9 @@ def extract( if not key.startswith(OT_BAGGAGE_PREFIX): continue - baggage[ - key[len(OT_BAGGAGE_PREFIX) :] - ] = _extract_first_element(getter.get(carrier, key)) + baggage[key[len(OT_BAGGAGE_PREFIX) :]] = ( + _extract_first_element(getter.get(carrier, key)) + ) for key, value in baggage.items(): context = set_baggage(key, value, context) diff --git a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/app_service.py b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/app_service.py index 1e853acc57..b4daa4cc84 100644 --- a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/app_service.py +++ b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/app_service.py @@ -47,19 +47,19 @@ def detect(self) -> Resource: website_site_name = environ.get(_WEBSITE_SITE_NAME) if website_site_name: attributes[ResourceAttributes.SERVICE_NAME] = website_site_name - attributes[ - ResourceAttributes.CLOUD_PROVIDER - ] = CloudProviderValues.AZURE.value - attributes[ - ResourceAttributes.CLOUD_PLATFORM - ] = CloudPlatformValues.AZURE_APP_SERVICE.value + attributes[ResourceAttributes.CLOUD_PROVIDER] = ( + CloudProviderValues.AZURE.value + ) + attributes[ResourceAttributes.CLOUD_PLATFORM] = ( + CloudPlatformValues.AZURE_APP_SERVICE.value + ) azure_resource_uri = _get_azure_resource_uri(website_site_name) if azure_resource_uri: - attributes[ - ResourceAttributes.CLOUD_RESOURCE_ID - ] = azure_resource_uri - for (key, env_var) in _APP_SERVICE_ATTRIBUTE_ENV_VARS.items(): + attributes[ResourceAttributes.CLOUD_RESOURCE_ID] = ( + azure_resource_uri + ) + for key, env_var in _APP_SERVICE_ATTRIBUTE_ENV_VARS.items(): value = environ.get(env_var) if value: attributes[key] = value diff --git a/scripts/check_for_valid_readme.py b/scripts/check_for_valid_readme.py index 42446dd741..11b1fa81ac 100644 --- a/scripts/check_for_valid_readme.py +++ b/scripts/check_for_valid_readme.py @@ -1,4 +1,5 @@ """Test script to check given paths for valid README.rst files.""" + import argparse import sys from pathlib import Path From 5375acf534f70f827d108ce2f2e7b8d728b2fc8e Mon Sep 17 00:00:00 2001 From: Alex Boten <223565+codeboten@users.noreply.github.com> Date: Thu, 25 Apr 2024 08:25:34 -0700 Subject: [PATCH 23/58] new(opentelemetry-processor-baggage): add new component (#2436) * new(opentelemetry-processor-baggage): add new component Fixes #2428 Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> * add tests Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> * update changelog Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> * update component owners Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> * lint Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> * update license Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> * fix lint Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> * lint Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> * rename processors dir to processor Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> --------- Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> --- .github/component_owners.yml | 3 + CHANGELOG.md | 2 + .../opentelemetry-processor-baggage/LICENSE | 201 ++++++++++++++++++ .../README.rst | 22 ++ .../pyproject.toml | 44 ++++ .../processor/baggage/__init__.py | 20 ++ .../processor/baggage/processor.py | 55 +++++ .../processor/baggage/version.py | 15 ++ .../test-requirements.txt | 2 + .../tests/__init__.py | 13 ++ .../tests/test_baggage_processor.py | 89 ++++++++ tox.ini | 8 + 12 files changed, 474 insertions(+) create mode 100644 processor/opentelemetry-processor-baggage/LICENSE create mode 100644 processor/opentelemetry-processor-baggage/README.rst create mode 100644 processor/opentelemetry-processor-baggage/pyproject.toml create mode 100644 processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/__init__.py create mode 100644 processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/processor.py create mode 100644 processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/version.py create mode 100644 processor/opentelemetry-processor-baggage/test-requirements.txt create mode 100644 processor/opentelemetry-processor-baggage/tests/__init__.py create mode 100644 processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py diff --git a/.github/component_owners.yml b/.github/component_owners.yml index ab14a41aec..efd15a6775 100644 --- a/.github/component_owners.yml +++ b/.github/component_owners.yml @@ -73,3 +73,6 @@ components: instrumentation/opentelemetry-instrumentation-psycopg: - federicobond + + processor/opentelemetry-processor-baggage: + - codeboten diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cbc913346..7d2a3e8314 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-pika` Instrumentation for `channel.consume()` (supported only for global, non channel specific instrumentation) ([#2397](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2397))) +- `opentelemetry-processor-baggage` Initial release + ([#2436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2436)) ### Fixed diff --git a/processor/opentelemetry-processor-baggage/LICENSE b/processor/opentelemetry-processor-baggage/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/processor/opentelemetry-processor-baggage/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/processor/opentelemetry-processor-baggage/README.rst b/processor/opentelemetry-processor-baggage/README.rst new file mode 100644 index 0000000000..2768758a99 --- /dev/null +++ b/processor/opentelemetry-processor-baggage/README.rst @@ -0,0 +1,22 @@ +OpenTelemetry Baggage Span Processor +==================================== + +The BaggageSpanProcessor reads entries stored in Baggage +from the parent context and adds the baggage entries' keys and +values to the span as attributes on span start. + +Add this span processor to a tracer provider. + +Keys and values added to Baggage will appear on subsequent child +spans for a trace within this service *and* be propagated to external +services in accordance with any configured propagation formats +configured. If the external services also have a Baggage span +processor, the keys and values will appear in those child spans as +well. + +⚠ Warning ⚠️ + +Do not put sensitive information in Baggage. + +To repeat: a consequence of adding data to Baggage is that the keys and +values will appear in all outgoing HTTP headers from the application. diff --git a/processor/opentelemetry-processor-baggage/pyproject.toml b/processor/opentelemetry-processor-baggage/pyproject.toml new file mode 100644 index 0000000000..0ef5392fdb --- /dev/null +++ b/processor/opentelemetry-processor-baggage/pyproject.toml @@ -0,0 +1,44 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "opentelemetry-processor-baggage" +dynamic = ["version"] +description = "OpenTelemetry Baggage Span Processor" +readme = "README.rst" +license = "Apache-2.0" +requires-python = ">=3.8" +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.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] +dependencies = [ + "opentelemetry-api ~= 1.5", + "wrapt >= 1.0.0, < 2.0.0", +] + +[project.urls] +Homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/processor/opentelemetry-processor-baggage" + +[tool.hatch.version] +path = "src/opentelemetry/processor/baggage/version.py" + +[tool.hatch.build.targets.sdist] +include = [ + "/src", + "/tests", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/opentelemetry"] diff --git a/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/__init__.py b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/__init__.py new file mode 100644 index 0000000000..a740c66491 --- /dev/null +++ b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/__init__.py @@ -0,0 +1,20 @@ +# 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=import-error + +from .processor import BaggageSpanProcessor +from .version import __version__ + +__all__ = ["BaggageSpanProcessor", "__version__"] diff --git a/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/processor.py b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/processor.py new file mode 100644 index 0000000000..36df06a94c --- /dev/null +++ b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/processor.py @@ -0,0 +1,55 @@ +# 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 typing import Optional + +from opentelemetry.baggage import get_all as get_all_baggage +from opentelemetry.context import Context +from opentelemetry.sdk.trace.export import SpanProcessor +from opentelemetry.trace import Span + + +class BaggageSpanProcessor(SpanProcessor): + """ + The BaggageSpanProcessor reads entries stored in Baggage + from the parent context and adds the baggage entries' keys and + values to the span as attributes on span start. + + Add this span processor to a tracer provider. + + Keys and values added to Baggage will appear on subsequent child + spans for a trace within this service *and* be propagated to external + services in accordance with any configured propagation formats + configured. If the external services also have a Baggage span + processor, the keys and values will appear in those child spans as + well. + + ⚠ Warning ⚠️ + + Do not put sensitive information in Baggage. + + To repeat: a consequence of adding data to Baggage is that the keys and + values will appear in all outgoing HTTP headers from the application. + + """ + + def __init__(self) -> None: + pass + + def on_start( + self, span: "Span", parent_context: Optional[Context] = None + ) -> None: + baggage = get_all_baggage(parent_context) + for key, value in baggage.items(): + span.set_attribute(key, value) diff --git a/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/version.py b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/version.py new file mode 100644 index 0000000000..ff4933b20b --- /dev/null +++ b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/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.46b0.dev" diff --git a/processor/opentelemetry-processor-baggage/test-requirements.txt b/processor/opentelemetry-processor-baggage/test-requirements.txt new file mode 100644 index 0000000000..fa7ad3d793 --- /dev/null +++ b/processor/opentelemetry-processor-baggage/test-requirements.txt @@ -0,0 +1,2 @@ + +-e processor/opentelemetry-processor-baggage \ No newline at end of file diff --git a/processor/opentelemetry-processor-baggage/tests/__init__.py b/processor/opentelemetry-processor-baggage/tests/__init__.py new file mode 100644 index 0000000000..b0a6f42841 --- /dev/null +++ b/processor/opentelemetry-processor-baggage/tests/__init__.py @@ -0,0 +1,13 @@ +# 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/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py b/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py new file mode 100644 index 0000000000..63a71c3cba --- /dev/null +++ b/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py @@ -0,0 +1,89 @@ +# 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 unittest + +from opentelemetry.baggage import get_all as get_all_baggage +from opentelemetry.baggage import set_baggage +from opentelemetry.context import attach, detach +from opentelemetry.processor.baggage import BaggageSpanProcessor +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import SpanProcessor +from opentelemetry.trace import Span, Tracer + + +class BaggageSpanProcessorTest(unittest.TestCase): + def test_check_the_baggage(self): + self.assertIsInstance(BaggageSpanProcessor(), SpanProcessor) + + def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_context( + self, + ): + tracer_provider = TracerProvider() + tracer_provider.add_span_processor(BaggageSpanProcessor()) + + # tracer has no baggage to start + tracer = tracer_provider.get_tracer("my-tracer") + self.assertIsInstance(tracer, Tracer) + self.assertEqual(get_all_baggage(), {}) + # set baggage in context + ctx = set_baggage("queen", "bee") + with tracer.start_as_current_span( + name="bumble", context=ctx + ) as bumble_span: + # span should have baggage key-value pair in context + self.assertEqual(get_all_baggage(ctx), {"queen": "bee"}) + # span should have baggage key-value pair in attribute + self.assertEqual(bumble_span._attributes["queen"], "bee") + with tracer.start_as_current_span( + name="child_span", context=ctx + ) as child_span: + self.assertIsInstance(child_span, Span) + # child span should have baggage key-value pair in context + self.assertEqual(get_all_baggage(ctx), {"queen": "bee"}) + # child span should have baggage key-value pair in attribute + self.assertEqual(child_span._attributes["queen"], "bee") + + def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_token( + self, + ): + tracer_provider = TracerProvider() + tracer_provider.add_span_processor(BaggageSpanProcessor()) + + # tracer has no baggage to start + tracer = tracer_provider.get_tracer("my-tracer") + self.assertIsInstance(tracer, Tracer) + self.assertEqual(get_all_baggage(), {}) + # create a context token and set baggage + honey_token = attach(set_baggage("bumble", "bee")) + self.assertEqual(get_all_baggage(), {"bumble": "bee"}) + # in a new span, ensure the baggage is there + with tracer.start_as_current_span("parent") as span: + self.assertEqual(get_all_baggage(), {"bumble": "bee"}) + self.assertEqual(span._attributes["bumble"], "bee") + # create a second context token and set more baggage + moar_token = attach(set_baggage("moar", "bee")) + self.assertEqual( + get_all_baggage(), {"bumble": "bee", "moar": "bee"} + ) + # in a child span, ensure all baggage is there as attributes + with tracer.start_as_current_span("child") as child_span: + self.assertEqual( + get_all_baggage(), {"bumble": "bee", "moar": "bee"} + ) + self.assertEqual(child_span._attributes["bumble"], "bee") + self.assertEqual(child_span._attributes["moar"], "bee") + detach(moar_token) + detach(honey_token) + self.assertEqual(get_all_baggage(), {}) diff --git a/tox.ini b/tox.ini index c9d7772601..0fb11855e8 100644 --- a/tox.ini +++ b/tox.ini @@ -287,6 +287,10 @@ envlist = py3{8,9,10,11}-test-instrumentation-cassandra pypy3-test-instrumentation-cassandra + ; opentelemetry-processor-baggage + py3{8,9,10,11}-test-processor-baggage + pypy3-test-processor-baggage + lint spellcheck docker-tests @@ -466,6 +470,8 @@ commands_pre = propagator-aws-xray: pip install -r {toxinidir}/propagator/opentelemetry-propagator-aws-xray/test-requirements.txt + processor-baggage: pip install -r {toxinidir}/processor/opentelemetry-processor-baggage/test-requirements.txt + ; we have to install packages in editable mode. coverage: python {toxinidir}/scripts/eachdist.py install --editable @@ -523,6 +529,7 @@ commands = test-util-http: pytest {toxinidir}/util/opentelemetry-util-http/tests {posargs} test-sdk-extension-aws: pytest {toxinidir}/sdk-extension/opentelemetry-sdk-extension-aws/tests {posargs} test-resource-detector-container: pytest {toxinidir}/resource/opentelemetry-resource-detector-container/tests {posargs} + test-processor-baggage: pytest {toxinidir}/processor/opentelemetry-processor-baggage/tests {posargs} test-propagator-aws: pytest {toxinidir}/propagator/opentelemetry-propagator-aws-xray/tests {posargs} test-propagator-ot-trace: pytest {toxinidir}/propagator/opentelemetry-propagator-ot-trace/tests {posargs} test-exporter-richconsole: pytest {toxinidir}/exporter/opentelemetry-exporter-richconsole/tests {posargs} @@ -622,6 +629,7 @@ commands_pre = pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-tortoiseorm/test-requirements.txt # requires snappy headers to be available on the system pip install -r {toxinidir}/resource/opentelemetry-resource-detector-container/test-requirements.txt + pip install -r {toxinidir}/processor/opentelemetry-processor-baggage/test-requirements.txt pip install -r {toxinidir}/propagator/opentelemetry-propagator-aws-xray/test-requirements.txt pip install -r {toxinidir}/propagator/opentelemetry-propagator-ot-trace/test-requirements.txt pip install -r {toxinidir}/sdk-extension/opentelemetry-sdk-extension-aws/test-requirements.txt From d5b5925cf8506a6f099394d1fbcfbe8e6ceaa8fe Mon Sep 17 00:00:00 2001 From: Alessandro Bologna Date: Thu, 25 Apr 2024 11:57:39 -0400 Subject: [PATCH 24/58] Fix exception handling for events with requestContext (#2418) * Fix exception handling for events with requestContext * added entry to changelog * reformatted with black --------- Co-authored-by: Diego Hurtado --- CHANGELOG.md | 2 ++ .../instrumentation/aws_lambda/__init__.py | 1 + .../test_aws_lambda_instrumentation_manual.py | 25 +++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d2a3e8314..3bcc845bcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2363](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2363)) - `opentelemetry-instrumentation-boto3sqs` Instrument Session and resource ([#2161](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2161)) +- `opentelemetry-instrumentation-aws-lambda` Fix exception handling for events with requestContext + ([#2418](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2418)) - Use sqlalchemy version in sqlalchemy commenter instead of opentelemetry library version ([#2404](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2404)) diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py index 8f29a0ec38..7614ba9813 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py @@ -365,6 +365,7 @@ def _instrumented_lambda_handler_call( # noqa pylint: disable=too-many-branches ) exception = None + result = None try: result = call_wrapped(*args, **kwargs) except Exception as exc: # pylint: disable=W0703 diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py index 51ebb67ebc..a2730028e8 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py @@ -436,6 +436,31 @@ def test_lambda_handles_handler_exception(self): exc_env_patch.stop() + def test_lambda_handles_handler_exception_with_api_gateway_proxy_event( + self, + ): + exc_env_patch = mock.patch.dict( + "os.environ", + {_HANDLER: "tests.mocks.lambda_function.handler_exc"}, + ) + exc_env_patch.start() + AwsLambdaInstrumentor().instrument() + # instrumentor re-raises the exception + with self.assertRaises(Exception): + mock_execute_lambda( + {"requestContext": {"http": {"method": "GET"}}} + ) + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + span = spans[0] + self.assertEqual(span.status.status_code, StatusCode.ERROR) + self.assertEqual(len(span.events), 1) + event = span.events[0] + self.assertEqual(event.name, "exception") + + exc_env_patch.stop() + def test_uninstrument(self): AwsLambdaInstrumentor().instrument() From c8d5f851ed212b2464594fa53ca28a803beff8f4 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 25 Apr 2024 10:40:03 -0700 Subject: [PATCH 25/58] HTTP transition for flask (#2454) --- CHANGELOG.md | 6 + instrumentation/README.md | 4 +- .../instrumentation/flask/__init__.py | 141 +++++++-- .../instrumentation/flask/package.py | 2 + .../tests/test_copy_context.py | 2 +- .../tests/test_programmatic.py | 290 ++++++++++++++++-- .../instrumentation/requests/__init__.py | 8 +- .../instrumentation/wsgi/package.py | 2 + .../tests/test_wsgi_middleware.py | 2 + .../opentelemetry/instrumentation/_semconv.py | 8 +- 10 files changed, 399 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bcc845bcc..ceb97433f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2372](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2372)) - Drop support for instrumenting elasticsearch client < 6` ([#2422](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2422)) +- `opentelemetry-instrumentation-wsgi` Add `http.method` to `span.name` + ([#2425](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2425)) +- `opentelemetry-instrumentation-flask` Add `http.method` to `span.name` + ([#2454](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2454)) ### Added @@ -23,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2382](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2382)) - `opentelemetry-instrumentation-wsgi` Implement new semantic convention opt-in with stable http semantic conventions ([#2425](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2425)) +- `opentelemetry-instrumentation-flask` Implement new semantic convention opt-in with stable http semantic conventions + ([#2454](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2454)) - `opentelemetry-instrumentation-threading` Initial release for threading ([#2253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2253)) - `opentelemetry-instrumentation-pika` Instrumentation for `channel.consume()` (supported diff --git a/instrumentation/README.md b/instrumentation/README.md index 284037d707..c73d0f7c0a 100644 --- a/instrumentation/README.md +++ b/instrumentation/README.md @@ -20,7 +20,7 @@ | [opentelemetry-instrumentation-elasticsearch](./opentelemetry-instrumentation-elasticsearch) | elasticsearch >= 2.0 | No | experimental | [opentelemetry-instrumentation-falcon](./opentelemetry-instrumentation-falcon) | falcon >= 1.4.1, < 4.0.0 | Yes | experimental | [opentelemetry-instrumentation-fastapi](./opentelemetry-instrumentation-fastapi) | fastapi ~= 0.58 | Yes | experimental -| [opentelemetry-instrumentation-flask](./opentelemetry-instrumentation-flask) | flask >= 1.0 | Yes | experimental +| [opentelemetry-instrumentation-flask](./opentelemetry-instrumentation-flask) | flask >= 1.0 | Yes | migration | [opentelemetry-instrumentation-grpc](./opentelemetry-instrumentation-grpc) | grpcio ~= 1.27 | No | experimental | [opentelemetry-instrumentation-httpx](./opentelemetry-instrumentation-httpx) | httpx >= 0.18.0 | No | experimental | [opentelemetry-instrumentation-jinja2](./opentelemetry-instrumentation-jinja2) | jinja2 >= 2.7, < 4.0 | No | experimental @@ -48,4 +48,4 @@ | [opentelemetry-instrumentation-tortoiseorm](./opentelemetry-instrumentation-tortoiseorm) | tortoise-orm >= 0.17.0 | No | experimental | [opentelemetry-instrumentation-urllib](./opentelemetry-instrumentation-urllib) | urllib | Yes | experimental | [opentelemetry-instrumentation-urllib3](./opentelemetry-instrumentation-urllib3) | urllib3 >= 1.0.0, < 3.0.0 | Yes | experimental -| [opentelemetry-instrumentation-wsgi](./opentelemetry-instrumentation-wsgi) | wsgi | Yes | experimental \ No newline at end of file +| [opentelemetry-instrumentation-wsgi](./opentelemetry-instrumentation-wsgi) | wsgi | Yes | migration \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py index a17f83a877..f2e0ee34cc 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py @@ -250,6 +250,15 @@ def response_hook(span: Span, status: str, response_headers: List): import opentelemetry.instrumentation.wsgi as otel_wsgi from opentelemetry import context, trace +from opentelemetry.instrumentation._semconv import ( + _METRIC_ATTRIBUTES_SERVER_DURATION_NAME, + _get_schema_url, + _HTTPStabilityMode, + _OpenTelemetrySemanticConventionStability, + _OpenTelemetryStabilitySignalType, + _report_new, + _report_old, +) from opentelemetry.instrumentation.flask.package import _instruments from opentelemetry.instrumentation.flask.version import __version__ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor @@ -260,7 +269,11 @@ def response_hook(span: Span, status: str, response_headers: List): from opentelemetry.metrics import get_meter from opentelemetry.semconv.metrics import MetricInstruments from opentelemetry.semconv.trace import SpanAttributes -from opentelemetry.util.http import get_excluded_urls, parse_excluded_urls +from opentelemetry.util.http import ( + get_excluded_urls, + parse_excluded_urls, + sanitize_method, +) _logger = getLogger(__name__) @@ -286,8 +299,13 @@ def _request_ctx_ref() -> weakref.ReferenceType: def get_default_span_name(): + method = sanitize_method( + flask.request.environ.get("REQUEST_METHOD", "").strip() + ) + if method == "_OTHER": + method = "HTTP" try: - span_name = flask.request.url_rule.rule + span_name = f"{method} {flask.request.url_rule.rule}" except AttributeError: span_name = otel_wsgi.get_default_span_name(flask.request.environ) return span_name @@ -296,9 +314,11 @@ def get_default_span_name(): def _rewrapped_app( wsgi_app, active_requests_counter, - duration_histogram, + duration_histogram_old=None, response_hook=None, excluded_urls=None, + sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, + duration_histogram_new=None, ): def _wrapped_app(wrapped_app_environ, start_response): # We want to measure the time for route matching, etc. @@ -307,11 +327,16 @@ def _wrapped_app(wrapped_app_environ, start_response): # we better avoid it. wrapped_app_environ[_ENVIRON_STARTTIME_KEY] = time_ns() start = default_timer() - attributes = otel_wsgi.collect_request_attributes(wrapped_app_environ) + attributes = otel_wsgi.collect_request_attributes( + wrapped_app_environ, sem_conv_opt_in_mode + ) active_requests_count_attrs = ( - otel_wsgi._parse_active_request_count_attrs(attributes) + otel_wsgi._parse_active_request_count_attrs( + attributes, + sem_conv_opt_in_mode, + ) ) - duration_attrs = otel_wsgi._parse_duration_attrs(attributes) + active_requests_counter.add(1, active_requests_count_attrs) def _start_response(status, response_headers, *args, **kwargs): @@ -330,13 +355,12 @@ def _start_response(status, response_headers, *args, **kwargs): if span: otel_wsgi.add_response_attributes( - span, status, response_headers + span, + status, + response_headers, + attributes, + sem_conv_opt_in_mode, ) - status_code = otel_wsgi._parse_status_code(status) - if status_code is not None: - duration_attrs[SpanAttributes.HTTP_STATUS_CODE] = ( - status_code - ) if ( span.is_recording() and span.kind == trace.SpanKind.SERVER @@ -357,8 +381,21 @@ def _start_response(status, response_headers, *args, **kwargs): return start_response(status, response_headers, *args, **kwargs) result = wsgi_app(wrapped_app_environ, _start_response) - duration = max(round((default_timer() - start) * 1000), 0) - duration_histogram.record(duration, duration_attrs) + duration_s = default_timer() - start + if duration_histogram_old: + duration_attrs_old = otel_wsgi._parse_duration_attrs( + attributes, _HTTPStabilityMode.DEFAULT + ) + duration_histogram_old.record( + max(round(duration_s * 1000), 0), duration_attrs_old + ) + if duration_histogram_new: + duration_attrs_new = otel_wsgi._parse_duration_attrs( + attributes, _HTTPStabilityMode.HTTP + ) + duration_histogram_new.record( + max(duration_s, 0), duration_attrs_new + ) active_requests_counter.add(-1, active_requests_count_attrs) return result @@ -371,6 +408,7 @@ def _wrapped_before_request( excluded_urls=None, enable_commenter=True, commenter_options=None, + sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, ): def _before_request(): if excluded_urls and excluded_urls.url_disabled(flask.request.url): @@ -379,7 +417,8 @@ def _before_request(): span_name = get_default_span_name() attributes = otel_wsgi.collect_request_attributes( - flask_request_environ + flask_request_environ, + sem_conv_opt_in_mode=sem_conv_opt_in_mode, ) if flask.request.url_rule: # For 404 that result from no route found, etc, we @@ -490,6 +529,7 @@ class _InstrumentedFlask(flask.Flask): _enable_commenter = True _commenter_options = None _meter_provider = None + _sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -503,11 +543,20 @@ def __init__(self, *args, **kwargs): _InstrumentedFlask._meter_provider, schema_url="https://opentelemetry.io/schemas/1.11.0", ) - duration_histogram = meter.create_histogram( - name=MetricInstruments.HTTP_SERVER_DURATION, - unit="ms", - description="Duration of HTTP client requests.", - ) + duration_histogram_old = None + if _report_old(_InstrumentedFlask._sem_conv_opt_in_mode): + duration_histogram_old = meter.create_histogram( + name=MetricInstruments.HTTP_SERVER_DURATION, + unit="ms", + description="measures the duration of the inbound HTTP request", + ) + duration_histogram_new = None + if _report_new(_InstrumentedFlask._sem_conv_opt_in_mode): + duration_histogram_new = meter.create_histogram( + name=_METRIC_ATTRIBUTES_SERVER_DURATION_NAME, + unit="s", + description="measures the duration of the inbound HTTP request", + ) active_requests_counter = meter.create_up_down_counter( name=MetricInstruments.HTTP_SERVER_ACTIVE_REQUESTS, unit="requests", @@ -517,9 +566,11 @@ def __init__(self, *args, **kwargs): self.wsgi_app = _rewrapped_app( self.wsgi_app, active_requests_counter, - duration_histogram, + duration_histogram_old, _InstrumentedFlask._response_hook, excluded_urls=_InstrumentedFlask._excluded_urls, + sem_conv_opt_in_mode=_InstrumentedFlask._sem_conv_opt_in_mode, + duration_histogram_new=duration_histogram_new, ) tracer = trace.get_tracer( @@ -535,6 +586,7 @@ def __init__(self, *args, **kwargs): excluded_urls=_InstrumentedFlask._excluded_urls, enable_commenter=_InstrumentedFlask._enable_commenter, commenter_options=_InstrumentedFlask._commenter_options, + sem_conv_opt_in_mode=_InstrumentedFlask._sem_conv_opt_in_mode, ) self._before_request = _before_request self.before_request(_before_request) @@ -578,11 +630,19 @@ def _instrument(self, **kwargs): _InstrumentedFlask._commenter_options = commenter_options meter_provider = kwargs.get("meter_provider") _InstrumentedFlask._meter_provider = meter_provider + + sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode( + _OpenTelemetryStabilitySignalType.HTTP, + ) + + _InstrumentedFlask._sem_conv_opt_in_mode = sem_conv_opt_in_mode + flask.Flask = _InstrumentedFlask def _uninstrument(self, **kwargs): flask.Flask = self._original_flask + # pylint: disable=too-many-locals @staticmethod def instrument_app( app, @@ -598,6 +658,11 @@ def instrument_app( app._is_instrumented_by_opentelemetry = False if not app._is_instrumented_by_opentelemetry: + # initialize semantic conventions opt-in if needed + _OpenTelemetrySemanticConventionStability._initialize() + sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode( + _OpenTelemetryStabilitySignalType.HTTP, + ) excluded_urls = ( parse_excluded_urls(excluded_urls) if excluded_urls is not None @@ -607,33 +672,44 @@ def instrument_app( __name__, __version__, meter_provider, - schema_url="https://opentelemetry.io/schemas/1.11.0", - ) - duration_histogram = meter.create_histogram( - name=MetricInstruments.HTTP_SERVER_DURATION, - unit="ms", - description="Duration of HTTP client requests.", + schema_url=_get_schema_url(sem_conv_opt_in_mode), ) + duration_histogram_old = None + if _report_old(sem_conv_opt_in_mode): + duration_histogram_old = meter.create_histogram( + name=MetricInstruments.HTTP_SERVER_DURATION, + unit="ms", + description="measures the duration of the inbound HTTP request", + ) + duration_histogram_new = None + if _report_new(sem_conv_opt_in_mode): + duration_histogram_new = meter.create_histogram( + name=_METRIC_ATTRIBUTES_SERVER_DURATION_NAME, + unit="s", + description="measures the duration of the inbound HTTP request", + ) active_requests_counter = meter.create_up_down_counter( name=MetricInstruments.HTTP_SERVER_ACTIVE_REQUESTS, - unit="requests", - description="measures the number of concurrent HTTP requests that are currently in-flight", + unit="{request}", + description="Number of active HTTP server requests.", ) app._original_wsgi_app = app.wsgi_app app.wsgi_app = _rewrapped_app( app.wsgi_app, active_requests_counter, - duration_histogram, - response_hook, + duration_histogram_old, + response_hook=response_hook, excluded_urls=excluded_urls, + sem_conv_opt_in_mode=sem_conv_opt_in_mode, + duration_histogram_new=duration_histogram_new, ) tracer = trace.get_tracer( __name__, __version__, tracer_provider, - schema_url="https://opentelemetry.io/schemas/1.11.0", + schema_url=_get_schema_url(sem_conv_opt_in_mode), ) _before_request = _wrapped_before_request( @@ -644,6 +720,7 @@ def instrument_app( commenter_options=( commenter_options if commenter_options else {} ), + sem_conv_opt_in_mode=sem_conv_opt_in_mode, ) app._before_request = _before_request app.before_request(_before_request) diff --git a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/package.py b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/package.py index d83adbede0..150ca0ca9e 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/package.py +++ b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/package.py @@ -16,3 +16,5 @@ _instruments = ("flask >= 1.0",) _supports_metrics = True + +_semconv_status = "migration" diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_copy_context.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_copy_context.py index 96268de5e7..7a57d01c43 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/test_copy_context.py +++ b/instrumentation/opentelemetry-instrumentation-flask/tests/test_copy_context.py @@ -44,5 +44,5 @@ def test_copycontext(self): resp = client.get("/copy_context", headers={"x-req": "a-header"}) self.assertEqual(200, resp.status_code) - self.assertEqual("/copy_context", resp.json["span_name"]) + self.assertEqual("GET /copy_context", resp.json["span_name"]) self.assertEqual("a-header", resp.json["request_header"]) diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py index 82ca88460c..d30a100b0e 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py +++ b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +# pylint: disable=too-many-lines from timeit import default_timer from unittest.mock import Mock, patch @@ -19,7 +20,12 @@ from opentelemetry import trace from opentelemetry.instrumentation._semconv import ( + _SPAN_ATTRIBUTES_ERROR_TYPE, + OTEL_SEMCONV_STABILITY_OPT_IN, + _OpenTelemetrySemanticConventionStability, + _server_active_requests_count_attrs_new, _server_active_requests_count_attrs_old, + _server_duration_attrs_new, _server_duration_attrs_old, ) from opentelemetry.instrumentation.flask import FlaskInstrumentor @@ -65,26 +71,71 @@ def expected_attributes(override_attributes): return default_attributes -_expected_metric_names = [ +def expected_attributes_new(override_attributes): + default_attributes = { + SpanAttributes.HTTP_REQUEST_METHOD: "GET", + SpanAttributes.SERVER_PORT: 80, + SpanAttributes.SERVER_ADDRESS: "localhost", + SpanAttributes.URL_PATH: "/hello/123", + SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.1", + SpanAttributes.HTTP_RESPONSE_STATUS_CODE: 200, + } + for key, val in override_attributes.items(): + default_attributes[key] = val + return default_attributes + + +_expected_metric_names_old = [ "http.server.active_requests", "http.server.duration", ] -_recommended_attrs = { +_expected_metric_names_new = [ + "http.server.active_requests", + "http.server.request.duration", +] +_recommended_metrics_attrs_old = { "http.server.active_requests": _server_active_requests_count_attrs_old, "http.server.duration": _server_duration_attrs_old, } +_recommended_metrics_attrs_new = { + "http.server.active_requests": _server_active_requests_count_attrs_new, + "http.server.request.duration": _server_duration_attrs_new, +} +_server_active_requests_count_attrs_both = ( + _server_active_requests_count_attrs_old +) +_server_active_requests_count_attrs_both.extend( + _server_active_requests_count_attrs_new +) +_recommended_metrics_attrs_both = { + "http.server.active_requests": _server_active_requests_count_attrs_both, + "http.server.duration": _server_duration_attrs_old, + "http.server.request.duration": _server_duration_attrs_new, +} +# pylint: disable=too-many-public-methods class TestProgrammatic(InstrumentationTest, WsgiTestBase): def setUp(self): super().setUp() + test_name = "" + if hasattr(self, "_testMethodName"): + test_name = self._testMethodName + sem_conv_mode = "default" + if "new_semconv" in test_name: + sem_conv_mode = "http" + elif "both_semconv" in test_name: + sem_conv_mode = "http/dup" + self.env_patch = patch.dict( "os.environ", { - "OTEL_PYTHON_FLASK_EXCLUDED_URLS": "http://localhost/env_excluded_arg/123,env_excluded_noarg" + "OTEL_PYTHON_FLASK_EXCLUDED_URLS": "http://localhost/env_excluded_arg/123,env_excluded_noarg", + OTEL_SEMCONV_STABILITY_OPT_IN: sem_conv_mode, }, ) + _OpenTelemetrySemanticConventionStability._initialized = False self.env_patch.start() self.exclude_patch = patch( @@ -170,7 +221,45 @@ def test_simple(self): span_list = self.memory_exporter.get_finished_spans() self.assertEqual(len(span_list), 1) - self.assertEqual(span_list[0].name, "/hello/") + self.assertEqual(span_list[0].name, "GET /hello/") + self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) + self.assertEqual(span_list[0].attributes, expected_attrs) + + def test_simple_new_semconv(self): + expected_attrs = expected_attributes_new( + { + SpanAttributes.HTTP_ROUTE: "/hello/", + SpanAttributes.URL_SCHEME: "http", + } + ) + self.client.get("/hello/123") + + span_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(span_list), 1) + self.assertEqual(span_list[0].name, "GET /hello/") + self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) + self.assertEqual(span_list[0].attributes, expected_attrs) + + def test_simple_both_semconv(self): + expected_attrs = expected_attributes( + { + SpanAttributes.HTTP_TARGET: "/hello/123", + SpanAttributes.HTTP_ROUTE: "/hello/", + } + ) + expected_attrs.update( + expected_attributes_new( + { + SpanAttributes.HTTP_ROUTE: "/hello/", + SpanAttributes.URL_SCHEME: "http", + } + ) + ) + self.client.get("/hello/123") + + span_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(span_list), 1) + self.assertEqual(span_list[0].name, "GET /hello/") self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) self.assertEqual(span_list[0].attributes, expected_attrs) @@ -220,6 +309,53 @@ def test_404(self): self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) self.assertEqual(span_list[0].attributes, expected_attrs) + def test_404_new_semconv(self): + expected_attrs = expected_attributes_new( + { + SpanAttributes.HTTP_REQUEST_METHOD: "POST", + SpanAttributes.HTTP_RESPONSE_STATUS_CODE: 404, + SpanAttributes.URL_PATH: "/bye", + SpanAttributes.URL_SCHEME: "http", + } + ) + + resp = self.client.post("/bye") + self.assertEqual(404, resp.status_code) + resp.close() + span_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(span_list), 1) + self.assertEqual(span_list[0].name, "POST /bye") + self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) + self.assertEqual(span_list[0].attributes, expected_attrs) + + def test_404_both_semconv(self): + expected_attrs = expected_attributes( + { + SpanAttributes.HTTP_METHOD: "POST", + SpanAttributes.HTTP_TARGET: "/bye", + SpanAttributes.HTTP_STATUS_CODE: 404, + } + ) + expected_attrs.update( + expected_attributes_new( + { + SpanAttributes.HTTP_REQUEST_METHOD: "POST", + SpanAttributes.HTTP_RESPONSE_STATUS_CODE: 404, + SpanAttributes.URL_PATH: "/bye", + SpanAttributes.URL_SCHEME: "http", + } + ) + ) + + resp = self.client.post("/bye") + self.assertEqual(404, resp.status_code) + resp.close() + span_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(span_list), 1) + self.assertEqual(span_list[0].name, "POST /bye") + self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) + self.assertEqual(span_list[0].attributes, expected_attrs) + def test_internal_error(self): expected_attrs = expected_attributes( { @@ -233,7 +369,53 @@ def test_internal_error(self): resp.close() span_list = self.memory_exporter.get_finished_spans() self.assertEqual(len(span_list), 1) - self.assertEqual(span_list[0].name, "/hello/") + self.assertEqual(span_list[0].name, "GET /hello/") + self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) + self.assertEqual(span_list[0].attributes, expected_attrs) + + def test_internal_error_new_semconv(self): + expected_attrs = expected_attributes_new( + { + SpanAttributes.URL_PATH: "/hello/500", + SpanAttributes.HTTP_ROUTE: "/hello/", + SpanAttributes.HTTP_RESPONSE_STATUS_CODE: 500, + _SPAN_ATTRIBUTES_ERROR_TYPE: "500", + SpanAttributes.URL_SCHEME: "http", + } + ) + resp = self.client.get("/hello/500") + self.assertEqual(500, resp.status_code) + resp.close() + span_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(span_list), 1) + self.assertEqual(span_list[0].name, "GET /hello/") + self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) + self.assertEqual(span_list[0].attributes, expected_attrs) + + def test_internal_error_both_semconv(self): + expected_attrs = expected_attributes( + { + SpanAttributes.HTTP_TARGET: "/hello/500", + SpanAttributes.HTTP_ROUTE: "/hello/", + SpanAttributes.HTTP_STATUS_CODE: 500, + } + ) + expected_attrs.update( + expected_attributes_new( + { + SpanAttributes.URL_PATH: "/hello/500", + SpanAttributes.HTTP_RESPONSE_STATUS_CODE: 500, + _SPAN_ATTRIBUTES_ERROR_TYPE: "500", + SpanAttributes.URL_SCHEME: "http", + } + ) + ) + resp = self.client.get("/hello/500") + self.assertEqual(500, resp.status_code) + resp.close() + span_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(span_list), 1) + self.assertEqual(span_list[0].name, "GET /hello/") self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) self.assertEqual(span_list[0].attributes, expected_attrs) @@ -291,7 +473,7 @@ def test_flask_metrics(self): for scope_metric in resource_metric.scope_metrics: self.assertTrue(len(scope_metric.metrics) != 0) for metric in scope_metric.metrics: - self.assertIn(metric.name, _expected_metric_names) + self.assertIn(metric.name, _expected_metric_names_old) data_points = list(metric.data.data_points) self.assertEqual(len(data_points), 1) for point in data_points: @@ -305,7 +487,42 @@ def test_flask_metrics(self): number_data_point_seen = True for attr in point.attributes: self.assertIn( - attr, _recommended_attrs[metric.name] + attr, + _recommended_metrics_attrs_old[metric.name], + ) + self.assertTrue(number_data_point_seen and histogram_data_point_seen) + + def test_flask_metrics_new_semconv(self): + start = default_timer() + self.client.get("/hello/123") + self.client.get("/hello/321") + self.client.get("/hello/756") + duration = max(round((default_timer() - start) * 1000), 0) + metrics_list = self.memory_metrics_reader.get_metrics_data() + number_data_point_seen = False + histogram_data_point_seen = False + self.assertTrue(len(metrics_list.resource_metrics) != 0) + for resource_metric in metrics_list.resource_metrics: + self.assertTrue(len(resource_metric.scope_metrics) != 0) + for scope_metric in resource_metric.scope_metrics: + self.assertTrue(len(scope_metric.metrics) != 0) + for metric in scope_metric.metrics: + self.assertIn(metric.name, _expected_metric_names_new) + data_points = list(metric.data.data_points) + self.assertEqual(len(data_points), 1) + for point in data_points: + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 3) + self.assertAlmostEqual( + duration, point.sum, delta=10 + ) + histogram_data_point_seen = True + if isinstance(point, NumberDataPoint): + number_data_point_seen = True + for attr in point.attributes: + self.assertIn( + attr, + _recommended_metrics_attrs_new[metric.name], ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) @@ -375,6 +592,23 @@ def test_basic_metric_success(self): expected_requests_count_attributes, ) + def test_basic_metric_success_new_semconv(self): + self.client.get("/hello/756") + expected_duration_attributes = { + "http.request.method": "GET", + "url.scheme": "http", + "network.protocol.version": "1.1", + "http.response.status_code": 200, + } + expected_requests_count_attributes = { + "http.request.method": "GET", + "url.scheme": "http", + } + self._assert_basic_metric( + expected_duration_attributes, + expected_requests_count_attributes, + ) + def test_basic_metric_nonstandard_http_method_success(self): self.client.open("/hello/756", method="NONSTANDARD") expected_duration_attributes = { @@ -401,32 +635,42 @@ def test_basic_metric_nonstandard_http_method_success(self): expected_requests_count_attributes, ) + def test_basic_metric_nonstandard_http_method_success_new_semconv(self): + self.client.open("/hello/756", method="NONSTANDARD") + expected_duration_attributes = { + "http.request.method": "_OTHER", + "url.scheme": "http", + "network.protocol.version": "1.1", + "http.response.status_code": 405, + } + expected_requests_count_attributes = { + "http.request.method": "_OTHER", + "url.scheme": "http", + } + self._assert_basic_metric( + expected_duration_attributes, + expected_requests_count_attributes, + ) + @patch.dict( "os.environ", { OTEL_PYTHON_INSTRUMENTATION_HTTP_CAPTURE_ALL_METHODS: "1", }, ) - def test_basic_metric_nonstandard_http_method_allowed_success(self): + def test_basic_metric_nonstandard_http_method_allowed_success_new_semconv( + self, + ): self.client.open("/hello/756", method="NONSTANDARD") expected_duration_attributes = { - "http.method": "NONSTANDARD", - "http.host": "localhost", - "http.scheme": "http", - "http.flavor": "1.1", - "http.server_name": "localhost", - "net.host.port": 80, - "http.status_code": 405, - "net.host.name": "localhost", + "http.request.method": "NONSTANDARD", + "url.scheme": "http", + "network.protocol.version": "1.1", + "http.response.status_code": 405, } expected_requests_count_attributes = { - "http.method": "NONSTANDARD", - "http.host": "localhost", - "http.scheme": "http", - "http.flavor": "1.1", - "http.server_name": "localhost", - "net.host.name": "localhost", - "net.host.port": 80, + "http.request.method": "NONSTANDARD", + "url.scheme": "http", } self._assert_basic_metric( expected_duration_attributes, diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index f468ff87ff..12797d6f5e 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -172,9 +172,11 @@ def get_or_create_headers(): try: parsed_url = urlparse(url) if parsed_url.scheme: - _set_http_scheme( - metric_labels, parsed_url.scheme, sem_conv_opt_in_mode - ) + if _report_old(sem_conv_opt_in_mode): + # TODO: Support opt-in for url.scheme in new semconv + _set_http_scheme( + metric_labels, parsed_url.scheme, sem_conv_opt_in_mode + ) if parsed_url.hostname: _set_http_host( metric_labels, parsed_url.hostname, sem_conv_opt_in_mode diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/package.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/package.py index 942f175da1..1bb8350a06 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/package.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/package.py @@ -16,3 +16,5 @@ _instruments = tuple() _supports_metrics = True + +_semconv_status = "migration" diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index b55ac6808f..2b26cbb5f9 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -241,6 +241,7 @@ def validate_response( SpanAttributes.SERVER_ADDRESS: "127.0.0.1", SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.0", SpanAttributes.HTTP_RESPONSE_STATUS_CODE: 200, + SpanAttributes.URL_SCHEME: "http", } if old_sem_conv: expected_attributes.update(expected_attributes_old) @@ -522,6 +523,7 @@ def test_request_attributes_new_semconv(self): SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.0", SpanAttributes.URL_PATH: "/", SpanAttributes.URL_QUERY: "foo=bar", + SpanAttributes.URL_SCHEME: "http", }, ) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index 8f236b5479..efe3c75f70 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -65,8 +65,7 @@ SpanAttributes.HTTP_RESPONSE_STATUS_CODE, SpanAttributes.HTTP_ROUTE, SpanAttributes.NETWORK_PROTOCOL_VERSION, - # TODO: Support opt-in for scheme in new semconv - # SpanAttributes.URL_SCHEME, + SpanAttributes.URL_SCHEME, ] _server_active_requests_count_attrs_old = [ @@ -234,9 +233,8 @@ def _set_http_url(result, url, sem_conv_opt_in_mode): def _set_http_scheme(result, scheme, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_SCHEME, scheme) - # TODO: Support opt-in for scheme in new semconv - # if _report_new(sem_conv_opt_in_mode): - # set_string_attribute(result, SpanAttributes.URL_SCHEME, scheme) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.URL_SCHEME, scheme) def _set_http_host(result, host, sem_conv_opt_in_mode): From bd4a22a0d92a33bb27034e1993d78707f75a4dfb Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 25 Apr 2024 16:23:22 -0500 Subject: [PATCH 26/58] Remove unnecessary package installations (#2424) * Remove unnecessary dependency installations Fixes #2423 * Fix tox.ini merge conflicts --- tox.ini | 247 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 231 insertions(+), 16 deletions(-) diff --git a/tox.ini b/tox.ini index 0fb11855e8..783f66e064 100644 --- a/tox.ini +++ b/tox.ini @@ -239,6 +239,10 @@ envlist = pypy3-test-instrumentation-tortoiseorm ; opentelemetry-instrumentation-httpx + ; The numbers at the end of the environment names + ; below mean these dependencies are being used: + ; 0: httpx>=0.18.0,<0.19.0 respx~=0.17.0 + ; 1: httpx>=0.19.0 respx~=0.20.1 py3{8,9,10,11}-test-instrumentation-httpx-{0,1} pypy3-test-instrumentation-httpx-{0,1} @@ -317,69 +321,144 @@ setenv = CORE_REPO=git+https://github.com/open-telemetry/opentelemetry-python.git@{env:CORE_REPO_SHA} commands_pre = -; Install without -e to test the actual installation - py3{8,9,10,11}: python -m pip install -U pip setuptools wheel -; Install common packages for all the tests. These are not needed in all the -; cases but it saves a lot of boilerplate in this file. - test: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api - test: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions - test: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk - test: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils - test: pip install {toxinidir}/opentelemetry-instrumentation - + opentelemetry-instrumentation: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + opentelemetry-instrumentation: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + opentelemetry-instrumentation: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + opentelemetry-instrumentation: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils opentelemetry-instrumentation: pip install -r {toxinidir}/opentelemetry-instrumentation/test-requirements.txt + distro: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + distro: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + distro: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + distro: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils distro: pip install -r {toxinidir}/opentelemetry-distro/test-requirements.txt + asgi: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + asgi: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + asgi: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + asgi: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils asgi: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi/test-requirements.txt + celery: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + celery: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + celery: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + celery: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils py3{8,9}-test-instrumentation-celery: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/test-requirements-0.txt py3{10,11}-test-instrumentation-celery: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/test-requirements-1.txt pypy3-test-instrumentation-celery: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/test-requirements-1.txt + sio-pika: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + sio-pika: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + sio-pika: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk sio-pika-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/test-requirements-0.txt sio-pika-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1.txt + aio-pika: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + aio-pika: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + aio-pika: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk aio-pika-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-0.txt aio-pika-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-1.txt aio-pika-2: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-2.txt aio-pika-3: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-3.txt + kafka-python: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + kafka-python: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + kafka-python: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk kafka-python: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-kafka-python/test-requirements.txt + confluent-kafka: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + confluent-kafka: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + confluent-kafka: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + confluent-kafka: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils confluent-kafka: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-confluent-kafka/test-requirements.txt + grpc: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + grpc: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + grpc: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + grpc: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils grpc: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc/test-requirements.txt + wsgi: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + wsgi: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + wsgi: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + wsgi: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils wsgi: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi/test-requirements.txt + asyncpg: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + asyncpg: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + asyncpg: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + asyncpg: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils asyncpg: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements.txt + aws-lambda: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + aws-lambda: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + aws-lambda: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + aws-lambda: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils aws-lambda: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aws-lambda/test-requirements.txt + boto: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + boto: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + boto: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + boto: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils boto: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-boto/test-requirements.txt + boto3sqs: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + boto3sqs: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + boto3sqs: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + boto3sqs: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils boto3sqs: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-boto3sqs/test-requirements.txt + falcon: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + falcon: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + falcon: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + falcon: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils falcon-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-0.txt falcon-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-1.txt falcon-2: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-2.txt + flask: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + flask: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + flask: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + flask: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils flask-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-flask/test-requirements-0.txt flask-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-flask/test-requirements-1.txt flask-2: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-flask/test-requirements-2.txt + urllib: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + urllib: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + urllib: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + urllib: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils urllib: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib/test-requirements.txt + urllib3: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + urllib3: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + urllib3: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + urllib3: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils urllib3-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-0.txt urllib3-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-1.txt + botocore: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + botocore: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + botocore: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + botocore: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils botocore: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/test-requirements.txt + cassandra: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + cassandra: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + cassandra: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + cassandra: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils cassandra: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-cassandra/test-requirements.txt + dbapi: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + dbapi: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + dbapi: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + dbapi: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils dbapi: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements.txt + django: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + django: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + django: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + django: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils py3{8,9}-test-instrumentation-django-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-0.txt py3{8,9}-test-instrumentation-django-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-1.txt py3{8,9}-test-instrumentation-django-2: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-2.txt @@ -388,90 +467,226 @@ commands_pre = pypy3-test-instrumentation-django-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-0.txt pypy3-test-instrumentation-django-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-1.txt + fastapi: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + fastapi: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + fastapi: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + fastapi: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils fastapi: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-fastapi/test-requirements.txt + mysql: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + mysql: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + mysql: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + mysql: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils mysql: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-mysql/test-requirements.txt + mysqlclient: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + mysqlclient: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + mysqlclient: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + mysqlclient: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils mysqlclient: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-mysqlclient/test-requirements.txt + pymemcache: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + pymemcache: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + pymemcache: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + pymemcache: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils pymemcache-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-0.txt pymemcache-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-1.txt pymemcache-2: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-2.txt pymemcache-3: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-3.txt pymemcache-4: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-4.txt + pymongo: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + pymongo: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + pymongo: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + pymongo: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils pymongo: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymongo/test-requirements.txt + psycopg: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + psycopg: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + psycopg: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + psycopg: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils py3{8,9}-test-instrumentation-psycopg: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-0.txt py3{10,11}-test-instrumentation-psycopg: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-1.txt pypy3-test-instrumentation-psycopg: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-1.txt + psycopg2: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + psycopg2: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + psycopg2: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + psycopg2: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils psycopg2: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg2/test-requirements.txt + pymysql: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + pymysql: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + pymysql: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + pymysql: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils pymysql: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymysql/test-requirements.txt + pyramid: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + pyramid: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + pyramid: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + pyramid: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils pyramid: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pyramid/test-requirements.txt + sqlite3: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + sqlite3: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + sqlite3: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + sqlite3: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils sqlite3: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlite3/test-requirements.txt + redis: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + redis: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + redis: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + redis: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils redis: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-redis/test-requirements.txt + remoulade: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + remoulade: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + remoulade: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + remoulade: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils remoulade: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-remoulade/test-requirements.txt + requests: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + requests: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + requests: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + requests: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils requests: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-requests/test-requirements.txt + starlette: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + starlette: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + starlette: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + starlette: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils starlette: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-starlette/test-requirements.txt + system-metrics: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + system-metrics: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + system-metrics: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + system-metrics: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils system-metrics: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-system-metrics/test-requirements.txt + threading: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + threading: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + threading: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + threading: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils threading: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-threading/test-requirements.txt + tornado: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + tornado: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + tornado: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + tornado: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils tornado: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-tornado/test-requirements.txt + tortoiseorm: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + tortoiseorm: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + tortoiseorm: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + tortoiseorm: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils tortoiseorm: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-tortoiseorm/test-requirements.txt + jinja2: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + jinja2: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + jinja2: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + jinja2: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils jinja2: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-jinja2/test-requirements.txt + logging: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + logging: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + logging: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + logging: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils logging: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt + aiohttp-client: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + aiohttp-client: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + aiohttp-client: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + aiohttp-client: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils aiohttp-client: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-client/test-requirements.txt + aiohttp-server: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + aiohttp-server: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + aiohttp-server: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + aiohttp-server: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils aiohttp-server: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-server/test-requirements.txt + aiopg: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + aiopg: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + aiopg: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + aiopg: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils aiopg: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements.txt + richconsole: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + richconsole: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + richconsole: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + richconsole: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils richconsole: pip install -r {toxinidir}/exporter/opentelemetry-exporter-richconsole/test-requirements.txt + prometheus: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + prometheus: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + prometheus: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + prometheus: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils prometheus: pip install -r {toxinidir}/exporter/opentelemetry-exporter-prometheus-remote-write/test-requirements.txt + sklearn: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + sklearn: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + sklearn: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + sklearn: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils sklearn: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-sklearn/test-requirements.txt + sqlalchemy: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + sqlalchemy: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + sqlalchemy: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + sqlalchemy: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils sqlalchemy-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-0.txt sqlalchemy-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-1.txt + elasticsearch: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + elasticsearch: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + elasticsearch: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + elasticsearch: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils elasticsearch: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements.txt + asyncio: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + asyncio: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + asyncio: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + asyncio: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils asyncio: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncio/test-requirements.txt - ; The numbers at the end of the environment names - ; below mean these dependencies are being used: - ; 0: httpx>=0.18.0,<0.19.0 respx~=0.17.0 - ; 1: httpx>=0.19.0 respx~=0.20.1 + httpx: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + httpx: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + httpx: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + httpx: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils httpx-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-0.txt httpx-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1.txt + sdk-extension-aws: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + sdk-extension-aws: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + sdk-extension-aws: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + sdk-extension-aws: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils sdk-extension-aws: pip install -r {toxinidir}/sdk-extension/opentelemetry-sdk-extension-aws/test-requirements.txt + resource-detector-container: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + resource-detector-container: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + resource-detector-container: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + resource-detector-container: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils resource-detector-container: pip install -r {toxinidir}/resource/opentelemetry-resource-detector-container/test-requirements.txt - http: pip install {toxinidir}/util/opentelemetry-util-http -; In order to get a health coverage report, + propagator-ot-trace: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + propagator-ot-trace: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + propagator-ot-trace: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + propagator-ot-trace: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils propagator-ot-trace: pip install -r {toxinidir}/propagator/opentelemetry-propagator-ot-trace/test-requirements.txt + propagator-aws-xray: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + propagator-aws-xray: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + propagator-aws-xray: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + propagator-aws-xray: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils propagator-aws-xray: pip install -r {toxinidir}/propagator/opentelemetry-propagator-aws-xray/test-requirements.txt + processor-baggage: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api + processor-baggage: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + processor-baggage: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk processor-baggage: pip install -r {toxinidir}/processor/opentelemetry-processor-baggage/test-requirements.txt + http: pip install {toxinidir}/util/opentelemetry-util-http + +; In order to get a health coverage report, ; we have to install packages in editable mode. coverage: python {toxinidir}/scripts/eachdist.py install --editable From 3291f38e8dbde5d73ba2e0ba2a80fd3632faa1d7 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 28 Apr 2024 16:20:17 +0200 Subject: [PATCH 27/58] elasticsearch: test against elasticsearch 7 (#2431) * Update core repo SHA * elasticsearch: test against elasticsearch 7 --------- Co-authored-by: Diego Hurtado --- .github/workflows/test.yml | 2 +- ...quirements.txt => test-requirements-0.txt} | 2 +- .../test-requirements-1.txt | 22 +++ .../tests/helpers_es7.py | 2 +- .../tests/test_elasticsearch.py | 157 ++++++++++++------ tox.ini | 14 +- 6 files changed, 141 insertions(+), 58 deletions(-) rename instrumentation/opentelemetry-instrumentation-elasticsearch/{test-requirements.txt => test-requirements-0.txt} (96%) create mode 100644 instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 529f70e565..5647f34998 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,7 +6,7 @@ on: - 'release/*' pull_request: env: - CORE_REPO_SHA: 955c92e91b5cd4bcfb43c39efcef086b040471d2 + CORE_REPO_SHA: 47d5ad7aae5aef31238ca66e55dc550b307c7b35 jobs: misc: diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-0.txt similarity index 96% rename from instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements.txt rename to instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-0.txt index 4bd1d0d318..054c8a8047 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-0.txt @@ -15,7 +15,7 @@ python-dateutil==2.8.2 six==1.16.0 tomli==2.0.1 typing_extensions==4.10.0 -urllib3==2.2.1 +urllib3==1.26.18 wrapt==1.16.0 zipp==3.17.0 -e opentelemetry-instrumentation diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt new file mode 100644 index 0000000000..efa05fd7ff --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt @@ -0,0 +1,22 @@ +asgiref==3.7.2 +attrs==23.2.0 +Deprecated==1.2.14 +elasticsearch==7.17.9 +elasticsearch-dsl==7.4.1 +importlib-metadata==6.11.0 +iniconfig==2.0.0 +packaging==23.2 +pluggy==1.4.0 +py==1.11.0 +py-cpuinfo==9.0.0 +pytest==7.1.3 +pytest-benchmark==4.0.0 +python-dateutil==2.8.2 +six==1.16.0 +tomli==2.0.1 +typing_extensions==4.10.0 +urllib3==1.26.18 +wrapt==1.16.0 +zipp==3.17.0 +-e opentelemetry-instrumentation +-e instrumentation/opentelemetry-instrumentation-elasticsearch diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es7.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es7.py index a2d37a54a9..b22df18452 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es7.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es7.py @@ -26,6 +26,6 @@ class Index: } } dsl_index_result = (1, {}, '{"result": "created"}') -dsl_index_span_name = "Elasticsearch/test-index/_doc/2" +dsl_index_span_name = "Elasticsearch/test-index/_doc/:id" dsl_index_url = "/test-index/_doc/2" dsl_search_method = "POST" diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py index 5d1c85f77d..690cbe3d4c 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py @@ -50,6 +50,23 @@ # pylint: disable=too-many-public-methods +def normalize_arguments(doc_type, body=None): + if major_version == 7: + return {"document": body} if body else {} + return ( + {"body": body, "doc_type": doc_type} + if body + else {"doc_type": doc_type} + ) + + +def get_elasticsearch_client(*args, **kwargs): + client = Elasticsearch(*args, **kwargs) + if major_version == 7: + client.transport._verified_elasticsearch = True + return client + + @mock.patch( "elasticsearch.connection.http_urllib3.Urllib3HttpConnection.perform_request" ) @@ -79,10 +96,14 @@ def tearDown(self): ElasticsearchInstrumentor().uninstrument() def test_instrumentor(self, request_mock): - request_mock.return_value = (1, {}, {}) + request_mock.return_value = (1, {}, "{}") - es = Elasticsearch() - es.index(index="sw", doc_type="_doc", id=1, body={"name": "adam"}) + es = get_elasticsearch_client(hosts=["http://localhost:9200"]) + es.index( + index="sw", + id=1, + **normalize_arguments(body={"name": "adam"}, doc_type="_doc"), + ) spans_list = self.get_finished_spans() self.assertEqual(len(spans_list), 1) @@ -97,20 +118,24 @@ def test_instrumentor(self, request_mock): # check that no spans are generated after uninstrument ElasticsearchInstrumentor().uninstrument() - es.index(index="sw", doc_type="_doc", id=1, body={"name": "adam"}) + es.index( + index="sw", + id=1, + **normalize_arguments(body={"name": "adam"}, doc_type="_doc"), + ) spans_list = self.get_finished_spans() self.assertEqual(len(spans_list), 1) def test_span_not_recording(self, request_mock): - request_mock.return_value = (1, {}, {}) + request_mock.return_value = (1, {}, "{}") mock_tracer = mock.Mock() mock_span = mock.Mock() mock_span.is_recording.return_value = False mock_tracer.start_span.return_value = mock_span with mock.patch("opentelemetry.trace.get_tracer") as tracer: tracer.return_value = mock_tracer - Elasticsearch() + get_elasticsearch_client(hosts=["http://localhost:9200"]) self.assertFalse(mock_span.is_recording()) self.assertTrue(mock_span.is_recording.called) self.assertFalse(mock_span.set_attribute.called) @@ -122,7 +147,7 @@ def test_prefix_arg(self, request_mock): prefix = "prefix-from-env" ElasticsearchInstrumentor().uninstrument() ElasticsearchInstrumentor(span_name_prefix=prefix).instrument() - request_mock.return_value = (1, {}, {}) + request_mock.return_value = (1, {}, "{}") self._test_prefix(prefix) def test_prefix_env(self, request_mock): @@ -131,13 +156,17 @@ def test_prefix_env(self, request_mock): os.environ[env_var] = prefix ElasticsearchInstrumentor().uninstrument() ElasticsearchInstrumentor().instrument() - request_mock.return_value = (1, {}, {}) + request_mock.return_value = (1, {}, "{}") del os.environ[env_var] self._test_prefix(prefix) def _test_prefix(self, prefix): - es = Elasticsearch() - es.index(index="sw", doc_type="_doc", id=1, body={"name": "adam"}) + es = get_elasticsearch_client(hosts=["http://localhost:9200"]) + es.index( + index="sw", + id=1, + **normalize_arguments(body={"name": "adam"}, doc_type="_doc"), + ) spans_list = self.get_finished_spans() self.assertEqual(len(spans_list), 1) @@ -150,8 +179,10 @@ def test_result_values(self, request_mock): {}, '{"found": false, "timed_out": true, "took": 7}', ) - es = Elasticsearch() - es.get(index="test-index", doc_type="_doc", id=1) + es = get_elasticsearch_client(hosts=["http://localhost:9200"]) + es.get( + index="test-index", **normalize_arguments(doc_type="_doc"), id=1 + ) spans = self.get_finished_spans() @@ -171,14 +202,18 @@ def test_trace_error_unknown(self, request_mock): def test_trace_error_not_found(self, request_mock): msg = "record not found" exc = elasticsearch.exceptions.NotFoundError(404, msg) - request_mock.return_value = (1, {}, {}) + request_mock.return_value = (1, {}, "{}") request_mock.side_effect = exc self._test_trace_error(StatusCode.ERROR, exc) def _test_trace_error(self, code, exc): - es = Elasticsearch() + es = get_elasticsearch_client(hosts=["http://localhost:9200"]) try: - es.get(index="test-index", doc_type="_doc", id=1) + es.get( + index="test-index", + **normalize_arguments(doc_type="_doc"), + id=1, + ) except Exception: # pylint: disable=broad-except pass @@ -192,10 +227,14 @@ def _test_trace_error(self, code, exc): ) def test_parent(self, request_mock): - request_mock.return_value = (1, {}, {}) - es = Elasticsearch() + request_mock.return_value = (1, {}, "{}") + es = get_elasticsearch_client(hosts=["http://localhost:9200"]) with self.tracer.start_as_current_span("parent"): - es.index(index="sw", doc_type="_doc", id=1, body={"name": "adam"}) + es.index( + index="sw", + **normalize_arguments(doc_type="_doc", body={"name": "adam"}), + id=1, + ) spans = self.get_finished_spans() self.assertEqual(len(spans), 2) @@ -206,8 +245,8 @@ def test_parent(self, request_mock): self.assertEqual(child.parent.span_id, parent.context.span_id) def test_multithread(self, request_mock): - request_mock.return_value = (1, {}, {}) - es = Elasticsearch() + request_mock.return_value = (1, {}, "{}") + es = get_elasticsearch_client(hosts=["http://localhost:9200"]) ev = threading.Event() # 1. Start tracing from thread-1; make thread-2 wait @@ -215,13 +254,21 @@ def test_multithread(self, request_mock): # 3. Check the spans got different parents, and are in the expected order. def target1(parent_span): with trace.use_span(parent_span): - es.get(index="test-index", doc_type="_doc", id=1) + es.get( + index="test-index", + **normalize_arguments(doc_type="_doc"), + id=1, + ) ev.set() ev.wait() def target2(): ev.wait() - es.get(index="test-index", doc_type="_doc", id=2) + es.get( + index="test-index", + **normalize_arguments(doc_type="_doc"), + id=2, + ) ev.set() with self.tracer.start_as_current_span("parent") as span: @@ -247,7 +294,7 @@ def target2(): def test_dsl_search(self, request_mock): request_mock.return_value = (1, {}, '{"hits": {"hits": []}}') - client = Elasticsearch() + client = get_elasticsearch_client(hosts=["http://localhost:9200"]) search = Search(using=client, index="test-index").filter( "term", author="testing" ) @@ -264,7 +311,7 @@ def test_dsl_search(self, request_mock): def test_dsl_search_sanitized(self, request_mock): request_mock.return_value = (1, {}, '{"hits": {"hits": []}}') - client = Elasticsearch() + client = get_elasticsearch_client(hosts=["http://localhost:9200"]) search = Search(using=client, index="test-index").filter( "term", author="testing" ) @@ -280,8 +327,8 @@ def test_dsl_search_sanitized(self, request_mock): ) def test_dsl_create(self, request_mock): - request_mock.return_value = (1, {}, {}) - client = Elasticsearch() + request_mock.return_value = (1, {}, "{}") + client = get_elasticsearch_client(hosts=["http://localhost:9200"]) Article.init(using=client) spans = self.get_finished_spans() @@ -307,8 +354,8 @@ def test_dsl_create(self, request_mock): ) def test_dsl_create_sanitized(self, request_mock): - request_mock.return_value = (1, {}, {}) - client = Elasticsearch() + request_mock.return_value = (1, {}, "{}") + client = get_elasticsearch_client(hosts=["http://localhost:9200"]) Article.init(using=client) spans = self.get_finished_spans() @@ -323,9 +370,9 @@ def test_dsl_create_sanitized(self, request_mock): ) def test_dsl_index(self, request_mock): - request_mock.return_value = helpers.dsl_index_result + request_mock.return_value = (1, {}, helpers.dsl_index_result[2]) - client = Elasticsearch() + client = get_elasticsearch_client(hosts=["http://localhost:9200"]) article = Article( meta={"id": 2}, title="About searching", @@ -374,11 +421,16 @@ def request_hook(span, method, url, kwargs): {}, '{"found": false, "timed_out": true, "took": 7}', ) - es = Elasticsearch() + es = get_elasticsearch_client(hosts=["http://localhost:9200"]) index = "test-index" doc_id = 1 - kwargs = {"params": {"test": True}} - es.get(index=index, doc_type="_doc", id=doc_id, **kwargs) + kwargs = {"params": {"refresh": True, "realtime": True}} + es.get( + index=index, + id=doc_id, + **normalize_arguments(doc_type="_doc"), + **kwargs, + ) spans = self.get_finished_spans() @@ -386,12 +438,21 @@ def request_hook(span, method, url, kwargs): self.assertEqual( "GET", spans[0].attributes[request_hook_method_attribute] ) + expected_url = f"/{index}/_doc/{doc_id}" self.assertEqual( - f"/{index}/_doc/{doc_id}", + expected_url, spans[0].attributes[request_hook_url_attribute], ) + + if major_version == 7: + expected_kwargs = { + **kwargs, + "headers": {"accept": "application/json"}, + } + else: + expected_kwargs = {**kwargs} self.assertEqual( - json.dumps(kwargs), + json.dumps(expected_kwargs), spans[0].attributes[request_hook_kwargs_attribute], ) @@ -431,13 +492,11 @@ def response_hook(span, response): }, } - request_mock.return_value = ( - 1, - {}, - json.dumps(response_payload), + request_mock.return_value = (1, {}, json.dumps(response_payload)) + es = get_elasticsearch_client(hosts=["http://localhost:9200"]) + es.get( + index="test-index", **normalize_arguments(doc_type="_doc"), id=1 ) - es = Elasticsearch() - es.get(index="test-index", doc_type="_doc", id=1) spans = self.get_finished_spans() @@ -453,13 +512,11 @@ def test_no_op_tracer_provider(self, request_mock): tracer_provider=trace.NoOpTracerProvider() ) response_payload = '{"found": false, "timed_out": true, "took": 7}' - request_mock.return_value = ( - 1, - {}, - response_payload, + request_mock.return_value = (1, {}, response_payload) + es = get_elasticsearch_client(hosts=["http://localhost:9200"]) + res = es.get( + index="test-index", **normalize_arguments(doc_type="_doc"), id=1 ) - es = Elasticsearch() - res = es.get(index="test-index", doc_type="_doc", id=1) self.assertEqual( res.get("found"), json.loads(response_payload).get("found") ) @@ -486,11 +543,11 @@ def test_body_sanitization(self, _): ) def test_bulk(self, request_mock): - request_mock.return_value = (1, {}, "") + request_mock.return_value = (1, {}, "{}") - es = Elasticsearch() + es = get_elasticsearch_client(hosts=["http://localhost:9200"]) es.bulk( - [ + body=[ { "_op_type": "index", "_index": "sw", diff --git a/tox.ini b/tox.ini index 783f66e064..cbe9c7d981 100644 --- a/tox.ini +++ b/tox.ini @@ -75,9 +75,12 @@ envlist = ; pypy3-test-instrumentation-boto ; opentelemetry-instrumentation-elasticsearch - ; FIXME: Elasticsearch >=7 causes CI workflow tests to hang, see open-telemetry/opentelemetry-python-contrib#620 - py3{8,9,10,11}-test-instrumentation-elasticsearch - pypy3-test-instrumentation-elasticsearch + ; The numbers at the end of the environment names + ; below mean these dependencies are being used: + ; 0: elasticsearch-dsl==6.4.0 elasticsearch==6.8.2 + ; 1: elasticsearch-dsl==7.4.1 elasticsearch==7.17.9 + py3{8,9,10,11}-test-instrumentation-elasticsearch-{0,1} + pypy3-test-instrumentation-elasticsearch-{0,1} ; opentelemetry-instrumentation-falcon ; py310 does not work with falcon 1 @@ -640,7 +643,8 @@ commands_pre = elasticsearch: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions elasticsearch: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk elasticsearch: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils - elasticsearch: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements.txt + elasticsearch-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-0.txt + elasticsearch-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt asyncio: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api asyncio: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions @@ -830,7 +834,7 @@ commands_pre = # prerequisite: follow the instructions here https://github.com/PyMySQL/mysqlclient#install # for your OS to install the required dependencies pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-mysqlclient/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements.txt + pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-tornado/test-requirements.txt pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements.txt pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymongo/test-requirements.txt From 58f3d87b78cbd773c9aba34e4386510505de4375 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Mon, 29 Apr 2024 19:35:04 +0200 Subject: [PATCH 28/58] gen-requirements: Bump jinja to latest one (#2463) --- gen-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gen-requirements.txt b/gen-requirements.txt index 0f96f12a56..de84b72c1e 100644 --- a/gen-requirements.txt +++ b/gen-requirements.txt @@ -1,6 +1,6 @@ -c dev-requirements.txt astor==0.8.1 -jinja2~=2.7 +jinja2==3.1.3 markupsafe==2.0.1 isort black From 2493258af11df7f211567103635588f2813de7a3 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Mon, 29 Apr 2024 10:53:01 -0700 Subject: [PATCH 29/58] Ignore vm detector if already in other rps for azure resource detector (#2456) --- .../CHANGELOG.md | 12 +++- .../resource/detector/azure/__init__.py | 25 +++++++ .../resource/detector/azure/_constants.py | 68 +++++++++++++++++++ .../resource/detector/azure/_utils.py | 37 ++++++++++ .../resource/detector/azure/app_service.py | 24 ++----- .../resource/detector/azure/vm.py | 43 +++++------- .../tests/test_vm.py | 10 +++ 7 files changed, 174 insertions(+), 45 deletions(-) create mode 100644 resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/__init__.py create mode 100644 resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_constants.py create mode 100644 resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_utils.py diff --git a/resource/opentelemetry-resource-detector-azure/CHANGELOG.md b/resource/opentelemetry-resource-detector-azure/CHANGELOG.md index f92a5db8b1..8954fc5359 100644 --- a/resource/opentelemetry-resource-detector-azure/CHANGELOG.md +++ b/resource/opentelemetry-resource-detector-azure/CHANGELOG.md @@ -7,9 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Ignore vm detector if already in other rps + ([#2456](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2456)) + +## Version 0.1.4 (2024-04-05) + +- Fix windows tests/suppress instrumentation for urllib call + ([#2178](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2178)) + +## Version 0.1.3 (2024-01-25) + - Change meta data service timeout to 200ms ([#2387](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2387)) -## Version 0.1.2 (2024-01-25) +## Version 0.1.1 (2024-01-10) - Initial CHANGELOG.md entry diff --git a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/__init__.py b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/__init__.py new file mode 100644 index 0000000000..913b677c3e --- /dev/null +++ b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/__init__.py @@ -0,0 +1,25 @@ +# 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=import-error + +from .app_service import AzureAppServiceResourceDetector +from .version import __version__ +from .vm import AzureVMResourceDetector + +__all__ = [ + "AzureAppServiceResourceDetector", + "AzureVMResourceDetector", + "__version__", +] diff --git a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_constants.py b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_constants.py new file mode 100644 index 0000000000..dddc6632ac --- /dev/null +++ b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_constants.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. + +from opentelemetry.semconv.resource import ResourceAttributes + +# cSpell:disable + +# Azure Kubernetes + +_AKS_ARM_NAMESPACE_ID = "AKS_ARM_NAMESPACE_ID" + +# AppService + +_AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE = "azure.app.service.stamp" +_REGION_NAME = "REGION_NAME" +_WEBSITE_HOME_STAMPNAME = "WEBSITE_HOME_STAMPNAME" +_WEBSITE_HOSTNAME = "WEBSITE_HOSTNAME" +_WEBSITE_INSTANCE_ID = "WEBSITE_INSTANCE_ID" +_WEBSITE_OWNER_NAME = "WEBSITE_OWNER_NAME" +_WEBSITE_RESOURCE_GROUP = "WEBSITE_RESOURCE_GROUP" +_WEBSITE_SITE_NAME = "WEBSITE_SITE_NAME" +_WEBSITE_SLOT_NAME = "WEBSITE_SLOT_NAME" + +_APP_SERVICE_ATTRIBUTE_ENV_VARS = { + ResourceAttributes.CLOUD_REGION: _REGION_NAME, + ResourceAttributes.DEPLOYMENT_ENVIRONMENT: _WEBSITE_SLOT_NAME, + ResourceAttributes.HOST_ID: _WEBSITE_HOSTNAME, + ResourceAttributes.SERVICE_INSTANCE_ID: _WEBSITE_INSTANCE_ID, + _AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE: _WEBSITE_HOME_STAMPNAME, +} + +# Functions + +_FUNCTIONS_WORKER_RUNTIME = "FUNCTIONS_WORKER_RUNTIME" + +# Vm + +_AZURE_VM_METADATA_ENDPOINT = "http://169.254.169.254/metadata/instance/compute?api-version=2021-12-13&format=json" +_AZURE_VM_SCALE_SET_NAME_ATTRIBUTE = "azure.vm.scaleset.name" +_AZURE_VM_SKU_ATTRIBUTE = "azure.vm.sku" + +_EXPECTED_AZURE_AMS_ATTRIBUTES = [ + _AZURE_VM_SCALE_SET_NAME_ATTRIBUTE, + _AZURE_VM_SKU_ATTRIBUTE, + ResourceAttributes.CLOUD_PLATFORM, + ResourceAttributes.CLOUD_PROVIDER, + ResourceAttributes.CLOUD_REGION, + ResourceAttributes.CLOUD_RESOURCE_ID, + ResourceAttributes.HOST_ID, + ResourceAttributes.HOST_NAME, + ResourceAttributes.HOST_TYPE, + ResourceAttributes.OS_TYPE, + ResourceAttributes.OS_VERSION, + ResourceAttributes.SERVICE_INSTANCE_ID, +] + +# cSpell:enable diff --git a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_utils.py b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_utils.py new file mode 100644 index 0000000000..3f73613945 --- /dev/null +++ b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_utils.py @@ -0,0 +1,37 @@ +# 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 + +from ._constants import ( + _AKS_ARM_NAMESPACE_ID, + _FUNCTIONS_WORKER_RUNTIME, + _WEBSITE_SITE_NAME, +) + + +def _is_on_aks() -> bool: + return os.environ.get(_AKS_ARM_NAMESPACE_ID) is not None + + +def _is_on_app_service() -> bool: + return os.environ.get(_WEBSITE_SITE_NAME) is not None + + +def _is_on_functions() -> bool: + return os.environ.get(_FUNCTIONS_WORKER_RUNTIME) is not None + + +def _can_ignore_vm_detect() -> bool: + return _is_on_aks() or _is_on_app_service() or _is_on_functions() diff --git a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/app_service.py b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/app_service.py index b4daa4cc84..613d8f9410 100644 --- a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/app_service.py +++ b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/app_service.py @@ -21,24 +21,12 @@ ResourceAttributes, ) -_AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE = "azure.app.service.stamp" -_REGION_NAME = "REGION_NAME" -_WEBSITE_HOME_STAMPNAME = "WEBSITE_HOME_STAMPNAME" -_WEBSITE_HOSTNAME = "WEBSITE_HOSTNAME" -_WEBSITE_INSTANCE_ID = "WEBSITE_INSTANCE_ID" -_WEBSITE_OWNER_NAME = "WEBSITE_OWNER_NAME" -_WEBSITE_RESOURCE_GROUP = "WEBSITE_RESOURCE_GROUP" -_WEBSITE_SITE_NAME = "WEBSITE_SITE_NAME" -_WEBSITE_SLOT_NAME = "WEBSITE_SLOT_NAME" - - -_APP_SERVICE_ATTRIBUTE_ENV_VARS = { - ResourceAttributes.CLOUD_REGION: _REGION_NAME, - ResourceAttributes.DEPLOYMENT_ENVIRONMENT: _WEBSITE_SLOT_NAME, - ResourceAttributes.HOST_ID: _WEBSITE_HOSTNAME, - ResourceAttributes.SERVICE_INSTANCE_ID: _WEBSITE_INSTANCE_ID, - _AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE: _WEBSITE_HOME_STAMPNAME, -} +from ._constants import ( + _APP_SERVICE_ATTRIBUTE_ENV_VARS, + _WEBSITE_OWNER_NAME, + _WEBSITE_RESOURCE_GROUP, + _WEBSITE_SITE_NAME, +) class AzureAppServiceResourceDetector(ResourceDetector): diff --git a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/vm.py b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/vm.py index da4c6563a5..2112282949 100644 --- a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/vm.py +++ b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/vm.py @@ -30,40 +30,31 @@ ResourceAttributes, ) -_AZURE_VM_METADATA_ENDPOINT = "http://169.254.169.254/metadata/instance/compute?api-version=2021-12-13&format=json" -_AZURE_VM_SCALE_SET_NAME_ATTRIBUTE = "azure.vm.scaleset.name" -_AZURE_VM_SKU_ATTRIBUTE = "azure.vm.sku" -_logger = getLogger(__name__) - -EXPECTED_AZURE_AMS_ATTRIBUTES = [ +from ._constants import ( + _AZURE_VM_METADATA_ENDPOINT, _AZURE_VM_SCALE_SET_NAME_ATTRIBUTE, _AZURE_VM_SKU_ATTRIBUTE, - ResourceAttributes.CLOUD_PLATFORM, - ResourceAttributes.CLOUD_PROVIDER, - ResourceAttributes.CLOUD_REGION, - ResourceAttributes.CLOUD_RESOURCE_ID, - ResourceAttributes.HOST_ID, - ResourceAttributes.HOST_NAME, - ResourceAttributes.HOST_TYPE, - ResourceAttributes.OS_TYPE, - ResourceAttributes.OS_VERSION, - ResourceAttributes.SERVICE_INSTANCE_ID, -] + _EXPECTED_AZURE_AMS_ATTRIBUTES, +) +from ._utils import _can_ignore_vm_detect + +_logger = getLogger(__name__) class AzureVMResourceDetector(ResourceDetector): # pylint: disable=no-self-use def detect(self) -> "Resource": attributes = {} - token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True)) - metadata_json = _get_azure_vm_metadata() - if not metadata_json: - return Resource(attributes) - for attribute_key in EXPECTED_AZURE_AMS_ATTRIBUTES: - attributes[attribute_key] = _get_attribute_from_metadata( - metadata_json, attribute_key - ) - detach(token) + if not _can_ignore_vm_detect(): + token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True)) + metadata_json = _get_azure_vm_metadata() + if not metadata_json: + return Resource(attributes) + for attribute_key in _EXPECTED_AZURE_AMS_ATTRIBUTES: + attributes[attribute_key] = _get_attribute_from_metadata( + metadata_json, attribute_key + ) + detach(token) return Resource(attributes) diff --git a/resource/opentelemetry-resource-detector-azure/tests/test_vm.py b/resource/opentelemetry-resource-detector-azure/tests/test_vm.py index 86aa373d3c..56d7d39104 100644 --- a/resource/opentelemetry-resource-detector-azure/tests/test_vm.py +++ b/resource/opentelemetry-resource-detector-azure/tests/test_vm.py @@ -378,3 +378,13 @@ def test_windows(self, mock_urlopen): attributes = AzureVMResourceDetector().detect().attributes for attribute_key, attribute_value in WINDOWS_ATTRIBUTES.items(): self.assertEqual(attributes[attribute_key], attribute_value) + + @patch("opentelemetry.resource.detector.azure.vm._can_ignore_vm_detect") + @patch("opentelemetry.resource.detector.azure.vm.urlopen") + def test_in_another_rp(self, mock_urlopen, detect_mock): + mock_urlopen.return_value.__enter__.return_value.read.return_value = ( + LINUX_JSON + ) + detect_mock.return_value = True + attributes = AzureVMResourceDetector().detect().attributes + self.assertEqual(attributes, {}) From de7ce0fb2274aafdcf86d5a95825e8f34a4dd6ec Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Mon, 29 Apr 2024 20:49:08 +0200 Subject: [PATCH 30/58] Use local imports when importing from tests (#2464) --- .../tests/test_aws_lambda_instrumentation_manual.py | 12 +++++------- .../tests/test_client_interceptor.py | 4 +--- .../tests/test_client_interceptor_filter.py | 4 +--- .../tests/test_client_interceptor_hooks.py | 4 +--- .../tests/test_pymemcache.py | 5 ++--- 5 files changed, 10 insertions(+), 19 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py index a2730028e8..f10953c754 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py @@ -17,13 +17,6 @@ from typing import Any, Callable, Dict from unittest import mock -from tests.mocks.api_gateway_http_api_event import ( - MOCK_LAMBDA_API_GATEWAY_HTTP_API_EVENT, -) -from tests.mocks.api_gateway_proxy_event import ( - MOCK_LAMBDA_API_GATEWAY_PROXY_EVENT, -) - from opentelemetry.environment_variables import OTEL_PROPAGATORS from opentelemetry.instrumentation.aws_lambda import ( _HANDLER, @@ -45,6 +38,11 @@ TraceContextTextMapPropagator, ) +from .mocks.api_gateway_http_api_event import ( + MOCK_LAMBDA_API_GATEWAY_HTTP_API_EVENT, +) +from .mocks.api_gateway_proxy_event import MOCK_LAMBDA_API_GATEWAY_PROXY_EVENT + class MockLambdaContext: def __init__(self, aws_request_id, invoked_function_arn): diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py index 2436aca40c..38759352b3 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py @@ -14,9 +14,6 @@ # pylint:disable=cyclic-import import grpc -from tests.protobuf import ( # pylint: disable=no-name-in-module - test_server_pb2_grpc, -) import opentelemetry.instrumentation.grpc from opentelemetry import trace @@ -41,6 +38,7 @@ simple_method_future, ) from ._server import create_test_server +from .protobuf import test_server_pb2_grpc from .protobuf.test_server_pb2 import Request diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_filter.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_filter.py index 9a9aefad59..b6ae975dff 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_filter.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_filter.py @@ -17,9 +17,6 @@ from unittest import mock import grpc -from tests.protobuf import ( # pylint: disable=no-name-in-module - test_server_pb2_grpc, -) import opentelemetry.instrumentation.grpc from opentelemetry import trace @@ -44,6 +41,7 @@ simple_method_future, ) from ._server import create_test_server +from .protobuf import test_server_pb2_grpc from .protobuf.test_server_pb2 import Request diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_hooks.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_hooks.py index ca649f7bb1..aeecffc71c 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_hooks.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_hooks.py @@ -13,9 +13,6 @@ # limitations under the License. import grpc -from tests.protobuf import ( # pylint: disable=no-name-in-module - test_server_pb2_grpc, -) from opentelemetry import trace from opentelemetry.instrumentation.grpc import GrpcInstrumentorClient @@ -23,6 +20,7 @@ from ._client import simple_method from ._server import create_test_server +from .protobuf import test_server_pb2_grpc # User defined interceptor. Is used in the tests along with the opentelemetry client interceptor. diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/tests/test_pymemcache.py b/instrumentation/opentelemetry-instrumentation-pymemcache/tests/test_pymemcache.py index 35b672bac0..4e29091217 100644 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/tests/test_pymemcache.py +++ b/instrumentation/opentelemetry-instrumentation-pymemcache/tests/test_pymemcache.py @@ -24,15 +24,14 @@ MemcacheUnknownError, ) -# pylint: disable=import-error,no-name-in-module -from tests.utils import MockSocket, _str - from opentelemetry import trace as trace_api from opentelemetry.instrumentation.pymemcache import PymemcacheInstrumentor from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.test.test_base import TestBase from opentelemetry.trace import get_tracer +from .utils import MockSocket, _str + TEST_HOST = "localhost" TEST_PORT = 117711 From 5116305f77bcd4c8ab18ef302a4351bb5b724c1e Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 30 Apr 2024 09:05:49 -0500 Subject: [PATCH 31/58] Separate lint into several jobs (#2467) * Separate lint for flask * Seaparate lint for resource-detector-container * Add linting workflow * Update contributing documentation * Fix lint workflow * Add fix for resource-detector-container * Separate lint for sdk-extension-aws * Separate lint for distro * Separate lint for opentelemetry-instrumentation * Separate lint for aiohttp-client * Separate lint for aiohttp-server * Separate lint for aiopg * Separate lint for aws-lambda * Separate lint for botocore * Separate lint for boto3sqs * Separate lint for django * Separate lint for dbapi * Separate lint for boto * Separate lint for elasticsearch * Separate lint for falcon * Separate lint for fastapi * Separate lint for urllib * Separate lint for urllib3 * Separate lint for requests * Separate lint for starlette * Separate lint for jinja2 * Seaparate lint for logging and richconsole * Separate lint for prometheus-remote-write * Separate lint for mysql * Separate lint for mysqlclient * Separate lint for psycopg2 * Separate lint for psycopg * Separate lint for pymemcache * Separate lint for pymongo * Separate lint for pymysql * Separate lint for pyramid * Separate lint for asgi * Separate lint for asyncpg * Separate lint for sqlite3 * Separate lint for wsgi * Separate lint for grpc * Separate lint for sqlalchemy * Separate lint for redis * Separate lint for remoulade * Separate lint for celery and sklearn * Separate lint for system-metrics * Separate lint for threading * Separate lint for tornado * Separate lint for tortoiseorm * Separate lint for httpx * Separate lint for propagator-aws-xray * Remove lint * Separate lint for propagator-ot-trace * Separate lint for sio-pika * Separate lint for aio-pika * Fix aio and sio pika * Separate lint for kafka-python * Separate lint for confluent-kafka * Separate lint for asyncio * Separate lint for cassandra * Separate lint for processor-baggage * Remove lint test environment * Rename lint workflow file * Fix sio-pika lint testing package path * More fixes * Fix linting for opentelemetry-instrumentation * Add section for linting with 3.8 * Add missing lint install command for elasticsearch * Sorted packages --- .github/workflows/lint.yml | 121 +++++++++ .github/workflows/test.yml | 5 +- CONTRIBUTING.md | 6 +- tox.ini | 495 +++++++++++++++++++++++++++++++------ 4 files changed, 543 insertions(+), 84 deletions(-) create mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000000..39afb3ee96 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,121 @@ +name: Lint tests + +on: + push: + branches-ignore: + - 'release/*' + pull_request: +env: + CORE_REPO_SHA: 955c92e91b5cd4bcfb43c39efcef086b040471d2 + +jobs: + lint-3_11: + strategy: + fail-fast: false # ensures the entire test matrix is run, even if one permutation fails + matrix: + package: + - "distro" + - "exporter-prometheus-remote-write" + - "exporter-richconsole" + - "instrumentation-aio-pika" + - "instrumentation-aiohttp-client" + - "instrumentation-aiohttp-server" + - "instrumentation-aiopg" + - "instrumentation-asgi" + - "instrumentation-asyncio" + - "instrumentation-asyncpg" + - "instrumentation-aws-lambda" + - "instrumentation-boto" + - "instrumentation-boto3sqs" + - "instrumentation-botocore" + - "instrumentation-cassandra" + - "instrumentation-celery" + - "instrumentation-confluent-kafka" + - "instrumentation-dbapi" + - "instrumentation-django" + - "instrumentation-elasticsearch" + - "instrumentation-falcon" + - "instrumentation-fastapi" + - "instrumentation-flask" + - "instrumentation-grpc" + - "instrumentation-httpx" + - "instrumentation-jinja2" + - "instrumentation-kafka-python" + - "instrumentation-logging" + - "instrumentation-mysql" + - "instrumentation-mysqlclient" + - "instrumentation-psycopg" + - "instrumentation-psycopg2" + - "instrumentation-pymemcache" + - "instrumentation-pymongo" + - "instrumentation-pymysql" + - "instrumentation-pyramid" + - "instrumentation-redis" + - "instrumentation-remoulade" + - "instrumentation-requests" + - "instrumentation-sio-pika" + - "instrumentation-sqlalchemy" + - "instrumentation-sqlite3" + - "instrumentation-starlette" + - "instrumentation-system-metrics" + - "instrumentation-threading" + - "instrumentation-tornado" + - "instrumentation-tortoiseorm" + - "instrumentation-urllib" + - "instrumentation-urllib3" + - "instrumentation-wsgi" + - "opentelemetry-instrumentation" + - "processor-baggage" + - "propagator-aws-xray" + - "propagator-ot-trace" + - "resource-detector-container" + - "sdk-extension-aws" + os: [ubuntu-20.04] + runs-on: ubuntu-20.04 + steps: + - name: Checkout Contrib Repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: 3.11 + - name: Install tox + run: pip install tox + - name: Cache tox environment + # Preserves .tox directory between runs for faster installs + uses: actions/cache@v4 + with: + path: | + .tox + ~/.cache/pip + key: v7-build-tox-cache-${{ matrix.package }}-${{ hashFiles('tox.ini', 'gen-requirements.txt', 'dev-requirements.txt') }} + - name: run tox + run: tox -e lint-${{ matrix.package }} + + lint-3_8: + strategy: + fail-fast: false # ensures the entire test matrix is run, even if one permutation fails + matrix: + package: + - "instrumentation-sklearn" + os: [ubuntu-20.04] + runs-on: ubuntu-20.04 + steps: + - name: Checkout Contrib Repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + - name: Set up Python 3.8 + uses: actions/setup-python@v5 + with: + python-version: 3.8 + - name: Install tox + run: pip install tox + - name: Cache tox environment + # Preserves .tox directory between runs for faster installs + uses: actions/cache@v4 + with: + path: | + .tox + ~/.cache/pip + key: v7-build-tox-cache-${{ matrix.package }}-${{ hashFiles('tox.ini', 'gen-requirements.txt', 'dev-requirements.txt') }} + - name: run tox + run: tox -e lint-${{ matrix.package }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5647f34998..a129c6e7ce 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - tox-environment: [ "docker-tests", "spellcheck", "lint", "docs", "generate" ] + tox-environment: [ "docker-tests", "spellcheck", "docs", "generate" ] name: ${{ matrix.tox-environment }} runs-on: ubuntu-20.04 steps: @@ -25,9 +25,6 @@ jobs: python-version: "3.10" - name: Install tox run: pip install tox - - name: Install libsnappy-dev - if: ${{ matrix.tox-environment == 'lint' }} - run: sudo apt-get install -y libsnappy-dev - name: Cache tox environment # Preserves .tox directory between runs for faster installs uses: actions/cache@v4 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0c18392bc7..3c4bae0f47 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,13 +62,13 @@ You can run `tox` with the following arguments: - `tox -e py311-test-instrumentation-aiopg` to e.g. run the aiopg instrumentation unit tests under a specific Python version - `tox -e spellcheck` to run a spellcheck on all the code -- `tox -e lint` to run lint checks on all code +- `tox -e lint-some-package` to run lint checks on `some-package` `black` and `isort` are executed when `tox -e lint` is run. The reported errors can be tedious to fix manually. An easier way to do so is: -1. Run `.tox/lint/bin/black .` -2. Run `.tox/lint/bin/isort .` +1. Run `.tox/lint-some-package/bin/black .` +2. Run `.tox/lint-some-package/bin/isort .` See [`tox.ini`](https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/tox.ini) diff --git a/tox.ini b/tox.ini index cbe9c7d981..ed74e485cd 100644 --- a/tox.ini +++ b/tox.ini @@ -9,44 +9,54 @@ envlist = ; opentelemetry-resource-detector-container py3{8,9,10,11}-test-resource-detector-container pypy3-test-resource-detector-container + lint-resource-detector-container ; opentelemetry-sdk-extension-aws py3{8,9,10,11}-test-sdk-extension-aws pypy3-test-sdk-extension-aws + lint-sdk-extension-aws ; opentelemetry-distro py3{8,9,10,11}-test-distro pypy3-test-distro + lint-distro ; opentelemetry-instrumentation py3{8,9,10,11}-test-opentelemetry-instrumentation pypy3-test-opentelemetry-instrumentation + lint-opentelemetry-instrumentation ; opentelemetry-instrumentation-aiohttp-client py3{8,9,10,11}-test-instrumentation-aiohttp-client pypy3-test-instrumentation-aiohttp-client + lint-instrumentation-aiohttp-client ; opentelemetry-instrumentation-aiohttp-server py3{8,9,10,11}-test-instrumentation-aiohttp-server pypy3-test-instrumentation-aiohttp-server + lint-instrumentation-aiohttp-server ; opentelemetry-instrumentation-aiopg py3{8,9,10,11}-test-instrumentation-aiopg ; instrumentation-aiopg intentionally excluded from pypy3 + lint-instrumentation-aiopg ; opentelemetry-instrumentation-aws-lambda py3{8,9,10,11}-test-instrumentation-aws-lambda pypy3-test-instrumentation-aws-lambda + lint-instrumentation-aws-lambda ; opentelemetry-instrumentation-botocore py3{8,9,10,11}-test-instrumentation-botocore ; FIXME: see https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1736 ; pypy3-test-instrumentation-botocore + lint-instrumentation-botocore ; opentelemetry-instrumentation-boto3sqs py3{8,9,10,11}-test-instrumentation-boto3sqs ; FIXME: see https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1736 ; pypy3-test-instrumentation-boto3sqs + lint-instrumentation-boto3sqs ; opentelemetry-instrumentation-django ; Only officially supported Python versions are tested for each Django @@ -64,15 +74,18 @@ envlist = py3{10,11}-test-instrumentation-django-1 py3{10,11}-test-instrumentation-django-3 pypy3-test-instrumentation-django-{0,1} + lint-instrumentation-django ; opentelemetry-instrumentation-dbapi py3{8,9,10,11}-test-instrumentation-dbapi pypy3-test-instrumentation-dbapi + lint-instrumentation-dbapi ; opentelemetry-instrumentation-boto py3{8,9,10,11}-test-instrumentation-boto ; FIXME: see https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1736 ; pypy3-test-instrumentation-boto + lint-instrumentation-boto ; opentelemetry-instrumentation-elasticsearch ; The numbers at the end of the environment names @@ -81,6 +94,7 @@ envlist = ; 1: elasticsearch-dsl==7.4.1 elasticsearch==7.17.9 py3{8,9,10,11}-test-instrumentation-elasticsearch-{0,1} pypy3-test-instrumentation-elasticsearch-{0,1} + lint-instrumentation-elasticsearch ; opentelemetry-instrumentation-falcon ; py310 does not work with falcon 1 @@ -92,10 +106,12 @@ envlist = py3{8,9}-test-instrumentation-falcon-0 py3{8,9,10,11}-test-instrumentation-falcon-{1,2} pypy3-test-instrumentation-falcon-{0,1,2} + lint-instrumentation-falcon ; opentelemetry-instrumentation-fastapi py3{8,9,10,11}-test-instrumentation-fastapi pypy3-test-instrumentation-fastapi + lint-instrumentation-fastapi ; opentelemetry-instrumentation-flask ; The numbers at the end of the environment names @@ -106,10 +122,12 @@ envlist = py3{8,9,10,11}-test-instrumentation-flask-{0,1} py3{8,9,10,11}-test-instrumentation-flask-{2} pypy3-test-instrumentation-flask-{0,1} + lint-instrumentation-flask ; opentelemetry-instrumentation-urllib py3{8,9,10,11}-test-instrumentation-urllib pypy3-test-instrumentation-urllib + lint-instrumentation-urllib ; opentelemetry-instrumentation-urllib3 ; The numbers at the end of the environment names @@ -118,46 +136,60 @@ envlist = ; 1: urllib3 >=2.0.0,<3.0.0 py3{8,9,10,11}-test-instrumentation-urllib3-{0,1} pypy3-test-instrumentation-urllib3-{0,1} + lint-instrumentation-urllib3 ; opentelemetry-instrumentation-requests py3{8,9,10,11}-test-instrumentation-requests ;pypy3-test-instrumentation-requests + lint-instrumentation-requests ; opentelemetry-instrumentation-starlette py3{8,9,10,11}-test-instrumentation-starlette pypy3-test-instrumentation-starlette + lint-instrumentation-starlette ; opentelemetry-instrumentation-jinja2 py3{8,9,10,11}-test-instrumentation-jinja2 pypy3-test-instrumentation-jinja2 + lint-instrumentation-jinja2 ; opentelemetry-instrumentation-logging py3{8,9,10,11}-test-instrumentation-logging pypy3-test-instrumentation-logging + lint-instrumentation-logging ; opentelemetry-exporter-richconsole py3{8,9,10,11}-test-exporter-richconsole pypy3-test-exporter-richconsole + lint-exporter-richconsole ; opentelemetry-exporter-prometheus-remote-write py3{8,9,10,11}-test-exporter-prometheus-remote-write pypy3-test-exporter-prometheus-remote-write + lint-exporter-prometheus-remote-write ; opentelemetry-instrumentation-mysql py3{8,9,10,11}-test-instrumentation-mysql pypy3-test-instrumentation-mysql + lint-instrumentation-mysql ; opentelemetry-instrumentation-mysqlclient py3{8,9,10,11}-test-instrumentation-mysqlclient pypy3-test-instrumentation-mysqlclient + ; prerequisite: follow the instructions here + ; https://github.com/PyMySQL/mysqlclient#install + ; for your OS to install the required dependencies + lint-instrumentation-mysqlclient ; opentelemetry-instrumentation-psycopg2 py3{8,9,10,11}-test-instrumentation-psycopg2 ; ext-psycopg2 intentionally excluded from pypy3 + lint-instrumentation-psycopg2 ; opentelemetry-instrumentation-psycopg py3{8,9,10,11}-test-instrumentation-psycopg pypy3-test-instrumentation-psycopg + lint-instrumentation-psycopg ; opentelemetry-instrumentation-pymemcache ; The numbers at the end of the environment names @@ -169,38 +201,47 @@ envlist = ; 4: pymemcache ==4.0.0 py3{8,9,10,11}-test-instrumentation-pymemcache-{0,1,2,3,4} pypy3-test-instrumentation-pymemcache-{0,1,2,3,4} + lint-instrumentation-pymemcache ; opentelemetry-instrumentation-pymongo py3{8,9,10,11}-test-instrumentation-pymongo pypy3-test-instrumentation-pymongo + lint-instrumentation-pymongo ; opentelemetry-instrumentation-pymysql py3{8,9,10,11}-test-instrumentation-pymysql pypy3-test-instrumentation-pymysql + lint-instrumentation-pymysql ; opentelemetry-instrumentation-pyramid py3{8,9,10,11}-test-instrumentation-pyramid pypy3-test-instrumentation-pyramid + lint-instrumentation-pyramid ; opentelemetry-instrumentation-asgi py3{8,9,10,11}-test-instrumentation-asgi pypy3-test-instrumentation-asgi + lint-instrumentation-asgi ; opentelemetry-instrumentation-asyncpg py3{8,9,10,11}-test-instrumentation-asyncpg ; ext-asyncpg intentionally excluded from pypy3 + lint-instrumentation-asyncpg ; opentelemetry-instrumentation-sqlite3 py3{8,9,10,11}-test-instrumentation-sqlite3 pypy3-test-instrumentation-sqlite3 + lint-instrumentation-sqlite3 ; opentelemetry-instrumentation-wsgi py3{8,9,10,11}-test-instrumentation-wsgi pypy3-test-instrumentation-wsgi + lint-instrumentation-wsgi ; opentelemetry-instrumentation-grpc py3{8,9,10,11}-test-instrumentation-grpc pypy3-test-instrumentation-grpc + lint-instrumentation-grpc ; opentelemetry-instrumentation-sqlalchemy ; The numbers at the end of the environment names @@ -209,37 +250,46 @@ envlist = ; 1: sqlalchemy~=1.4 aiosqlite py3{8,9,10,11}-test-instrumentation-sqlalchemy-{1} pypy3-test-instrumentation-sqlalchemy-{0,1} + lint-instrumentation-sqlalchemy ; opentelemetry-instrumentation-redis py3{8,9,10,11}-test-instrumentation-redis pypy3-test-instrumentation-redis + lint-instrumentation-redis ; opentelemetry-instrumentation-remoulade py3{8,9,10,11}-test-instrumentation-remoulade ; instrumentation-remoulade intentionally excluded from pypy3 + lint-instrumentation-remoulade ; opentelemetry-instrumentation-celery py3{8,9,10,11}-test-instrumentation-celery pypy3-test-instrumentation-celery + lint-instrumentation-celery ; opentelemetry-instrumentation-sklearn py3{8}-test-instrumentation-sklearn + lint-instrumentation-sklearn ; opentelemetry-instrumentation-system-metrics py3{8,9,10,11}-test-instrumentation-system-metrics pypy3-test-instrumentation-system-metrics + lint-instrumentation-system-metrics ; opentelemetry-instrumentation-threading py3{8,9,10,11}-test-instrumentation-threading pypy3-test-instrumentation-threading + lint-instrumentation-threading ; opentelemetry-instrumentation-tornado py3{8,9,10,11}-test-instrumentation-tornado pypy3-test-instrumentation-tornado + lint-instrumentation-tornado ; opentelemetry-instrumentation-tortoiseorm py3{8,9,10,11}-test-instrumentation-tortoiseorm pypy3-test-instrumentation-tortoiseorm + lint-instrumentation-tortoiseorm ; opentelemetry-instrumentation-httpx ; The numbers at the end of the environment names @@ -248,6 +298,7 @@ envlist = ; 1: httpx>=0.19.0 respx~=0.20.1 py3{8,9,10,11}-test-instrumentation-httpx-{0,1} pypy3-test-instrumentation-httpx-{0,1} + lint-instrumentation-httpx ; opentelemetry-util-http py3{8,9,10,11}-test-util-http @@ -256,10 +307,12 @@ envlist = ; opentelemetry-propagator-aws-xray py3{8,9,10,11}-test-propagator-aws-xray pypy3-test-propagator-aws-xray + lint-propagator-aws-xray ; opentelemetry-propagator-ot-trace py3{8,9,10,11}-test-propagator-ot-trace pypy3-test-propagator-ot-trace + lint-propagator-ot-trace ; opentelemetry-instrumentation-sio-pika ; The numbers at the end of the environment names @@ -268,6 +321,7 @@ envlist = ; 1: pika>=1.0.0 py3{8,9,10,11}-test-instrumentation-sio-pika-{0,1} pypy3-test-instrumentation-sio-pika-{0,1} + lint-instrumentation-sio-pika ; opentelemetry-instrumentation-aio-pika ; The numbers at the end of the environment names @@ -278,27 +332,33 @@ envlist = ; 3: aio_pika==9.4.1 py3{8,9,10,11}-test-instrumentation-aio-pika-{0,1,2,3} pypy3-test-instrumentation-aio-pika-{0,1,2,3} + lint-instrumentation-aio-pika ; opentelemetry-instrumentation-kafka-python py3{8,9,10,11}-test-instrumentation-kafka-python pypy3-test-instrumentation-kafka-python + lint-instrumentation-kafka-python ; opentelemetry-instrumentation-confluent-kafka py3{8,9,10,11}-test-instrumentation-confluent-kafka pypy3-test-instrumentation-confluent-kafka + lint-instrumentation-confluent-kafka ; opentelemetry-instrumentation-asyncio py3{8,9,10,11}-test-instrumentation-asyncio + lint-instrumentation-asyncio ; opentelemetry-instrumentation-cassandra py3{8,9,10,11}-test-instrumentation-cassandra pypy3-test-instrumentation-cassandra + lint-instrumentation-cassandra ; opentelemetry-processor-baggage py3{8,9,10,11}-test-processor-baggage pypy3-test-processor-baggage + ; requires snappy headers to be available on the system + lint-processor-baggage - lint spellcheck docker-tests docs @@ -308,6 +368,7 @@ envlist = [testenv] deps = -c dev-requirements.txt + lint: -r dev-requirements.txt test: pytest test: pytest-benchmark coverage: pytest @@ -349,12 +410,14 @@ commands_pre = py3{8,9}-test-instrumentation-celery: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/test-requirements-0.txt py3{10,11}-test-instrumentation-celery: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/test-requirements-1.txt pypy3-test-instrumentation-celery: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/test-requirements-1.txt + lint-instrumentation-celery: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/test-requirements-1.txt sio-pika: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api sio-pika: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions sio-pika: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk sio-pika-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/test-requirements-0.txt sio-pika-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1.txt + lint-instrumentation-sio-pika: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1.txt aio-pika: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api aio-pika: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions @@ -363,6 +426,7 @@ commands_pre = aio-pika-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-1.txt aio-pika-2: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-2.txt aio-pika-3: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-3.txt + lint-instrumentation-aio-pika: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-3.txt kafka-python: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api kafka-python: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions @@ -418,6 +482,7 @@ commands_pre = falcon-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-0.txt falcon-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-1.txt falcon-2: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-2.txt + lint-instrumentation-falcon: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-2.txt flask: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api flask: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions @@ -426,6 +491,7 @@ commands_pre = flask-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-flask/test-requirements-0.txt flask-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-flask/test-requirements-1.txt flask-2: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-flask/test-requirements-2.txt + lint-instrumentation-flask: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-flask/test-requirements-2.txt urllib: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api urllib: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions @@ -439,6 +505,7 @@ commands_pre = urllib3: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils urllib3-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-0.txt urllib3-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-1.txt + lint-instrumentation-urllib3: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-1.txt botocore: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api botocore: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions @@ -469,6 +536,7 @@ commands_pre = py3{10,11}-test-instrumentation-django-3: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-3.txt pypy3-test-instrumentation-django-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-0.txt pypy3-test-instrumentation-django-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-1.txt + lint-instrumentation-django: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-3.txt fastapi: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api fastapi: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions @@ -497,6 +565,7 @@ commands_pre = pymemcache-2: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-2.txt pymemcache-3: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-3.txt pymemcache-4: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-4.txt + lint-instrumentation-pymemcache: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-4.txt pymongo: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api pymongo: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions @@ -511,6 +580,7 @@ commands_pre = py3{8,9}-test-instrumentation-psycopg: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-0.txt py3{10,11}-test-instrumentation-psycopg: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-1.txt pypy3-test-instrumentation-psycopg: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-1.txt + lint-instrumentation-psycopg: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-1.txt psycopg2: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api psycopg2: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions @@ -638,6 +708,7 @@ commands_pre = sqlalchemy: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils sqlalchemy-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-0.txt sqlalchemy-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-1.txt + lint-instrumentation-sqlalchemy: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-1.txt elasticsearch: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api elasticsearch: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions @@ -645,6 +716,7 @@ commands_pre = elasticsearch: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils elasticsearch-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-0.txt elasticsearch-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt + lint-instrumentation-elasticsearch: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt asyncio: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api asyncio: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions @@ -658,6 +730,7 @@ commands_pre = httpx: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils httpx-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-0.txt httpx-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1.txt + lint-instrumentation-httpx: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1.txt sdk-extension-aws: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api sdk-extension-aws: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions @@ -696,63 +769,406 @@ commands_pre = commands = test-distro: pytest {toxinidir}/opentelemetry-distro/tests {posargs} + lint-distro: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/opentelemetry-distro + lint-distro: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/opentelemetry-distro + lint-distro: flake8 --config {toxinidir}/.flake8 {toxinidir}/opentelemetry-distro + lint-distro: pylint {toxinidir}/opentelemetry-distro/src/opentelemetry + lint-distro: pylint {toxinidir}/opentelemetry-distro/tests + test-opentelemetry-instrumentation: pytest {toxinidir}/opentelemetry-instrumentation/tests {posargs} + lint-opentelemetry-instrumentation: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/opentelemetry-instrumentation + lint-opentelemetry-instrumentation: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/opentelemetry-instrumentation + lint-opentelemetry-instrumentation: flake8 --config {toxinidir}/.flake8 {toxinidir}/opentelemetry-instrumentation + lint-opentelemetry-instrumentation: pylint {toxinidir}/opentelemetry-instrumentation/src/opentelemetry + lint-opentelemetry-instrumentation: pylint {toxinidir}/opentelemetry-instrumentation/tests + test-instrumentation-aiohttp-client: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests {posargs} + lint-instrumentation-aiohttp-client: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-client + lint-instrumentation-aiohttp-client: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-client + lint-instrumentation-aiohttp-client: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-client + lint-instrumentation-aiohttp-client: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry + lint-instrumentation-aiohttp-client: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests + test-instrumentation-aiohttp-server: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests {posargs} + lint-instrumentation-aiohttp-server: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-server + lint-instrumentation-aiohttp-server: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-server + lint-instrumentation-aiohttp-server: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-server + lint-instrumentation-aiohttp-server: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry + lint-instrumentation-aiohttp-server: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests + test-instrumentation-aiopg: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg/tests {posargs} + lint-instrumentation-aiopg: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg + lint-instrumentation-aiopg: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg + lint-instrumentation-aiopg: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg + lint-instrumentation-aiopg: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry + lint-instrumentation-aiopg: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg/tests + test-instrumentation-asgi: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi/tests {posargs} + lint-instrumentation-asgi: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi + lint-instrumentation-asgi: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi + lint-instrumentation-asgi: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi + lint-instrumentation-asgi: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry + lint-instrumentation-asgi: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi/tests + test-instrumentation-asyncpg: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg/tests {posargs} + lint-instrumentation-asyncpg: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg + lint-instrumentation-asyncpg: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg + lint-instrumentation-asyncpg: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg + lint-instrumentation-asyncpg: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry + lint-instrumentation-asyncpg: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg/tests + test-instrumentation-aws-lambda: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-aws-lambda/tests {posargs} + lint-instrumentation-aws-lambda: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-aws-lambda + lint-instrumentation-aws-lambda: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-aws-lambda + lint-instrumentation-aws-lambda: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-aws-lambda + lint-instrumentation-aws-lambda: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry + lint-instrumentation-aws-lambda: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-aws-lambda/tests + test-instrumentation-boto: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-boto/tests {posargs} + lint-instrumentation-boto: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-boto + lint-instrumentation-boto: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-boto + lint-instrumentation-boto: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-boto + lint-instrumentation-boto: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry + lint-instrumentation-boto: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-boto/tests + test-instrumentation-botocore: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/tests {posargs} + lint-instrumentation-botocore: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore + lint-instrumentation-botocore: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore + lint-instrumentation-botocore: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore + lint-instrumentation-botocore: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry + lint-instrumentation-botocore: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/tests + test-instrumentation-boto3sqs: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-boto3sqs/tests {posargs} + lint-instrumentation-boto3sqs: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-boto3sqs + lint-instrumentation-boto3sqs: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-boto3sqs + lint-instrumentation-boto3sqs: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-boto3sqs + lint-instrumentation-boto3sqs: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-boto3sqs/src/opentelemetry + lint-instrumentation-boto3sqs: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-boto3sqs/tests + test-instrumentation-cassandra: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-cassandra/tests {posargs} + lint-instrumentation-cassandra: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-cassandra + lint-instrumentation-cassandra: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-cassandra + lint-instrumentation-cassandra: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-cassandra + lint-instrumentation-cassandra: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-cassandra/src/opentelemetry + lint-instrumentation-cassandra: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-cassandra/tests + test-instrumentation-celery: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/tests {posargs} + lint-instrumentation-celery: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-celery + lint-instrumentation-celery: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-celery + lint-instrumentation-celery: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-celery + lint-instrumentation-celery: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry + lint-instrumentation-celery: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/tests + test-instrumentation-dbapi: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi/tests {posargs} + lint-instrumentation-dbapi: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi + lint-instrumentation-dbapi: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi + lint-instrumentation-dbapi: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi + lint-instrumentation-dbapi: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry + lint-instrumentation-dbapi: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi/tests + test-instrumentation-django: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-django/tests {posargs} + lint-instrumentation-django: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-django + lint-instrumentation-django: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-django + lint-instrumentation-django: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-django + lint-instrumentation-django: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry + lint-instrumentation-django: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-django/tests + test-instrumentation-elasticsearch: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/tests {posargs} + lint-instrumentation-elasticsearch: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch + lint-instrumentation-elasticsearch: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch + lint-instrumentation-elasticsearch: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch + lint-instrumentation-elasticsearch: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry + lint-instrumentation-elasticsearch: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/tests + test-instrumentation-falcon: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon/tests {posargs} + lint-instrumentation-falcon: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon + lint-instrumentation-falcon: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon + lint-instrumentation-falcon: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon + lint-instrumentation-falcon: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry + lint-instrumentation-falcon: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon/tests + test-instrumentation-fastapi: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-fastapi/tests {posargs} + lint-instrumentation-fastapi: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-fastapi + lint-instrumentation-fastapi: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-fastapi + lint-instrumentation-fastapi: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-fastapi + lint-instrumentation-fastapi: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry + lint-instrumentation-fastapi: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-fastapi/tests + test-instrumentation-flask: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-flask/tests {posargs} + lint-instrumentation-flask: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-flask + lint-instrumentation-flask: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-flask + lint-instrumentation-flask: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-flask + lint-instrumentation-flask: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry + lint-instrumentation-flask: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-flask/tests + test-instrumentation-urllib: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib/tests {posargs} + lint-instrumentation-urllib: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib + lint-instrumentation-urllib: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib + lint-instrumentation-urllib: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib + lint-instrumentation-urllib: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib/src/opentelemetry + lint-instrumentation-urllib: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib/tests + test-instrumentation-urllib3: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib3/tests {posargs} + lint-instrumentation-urllib3: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib3 + lint-instrumentation-urllib3: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib3 + lint-instrumentation-urllib3: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib3 + lint-instrumentation-urllib3: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib3/src/opentelemetry + lint-instrumentation-urllib3: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib3/tests + test-instrumentation-grpc: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc/tests {posargs} + lint-instrumentation-grpc: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc + lint-instrumentation-grpc: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc + lint-instrumentation-grpc: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc + lint-instrumentation-grpc: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry + lint-instrumentation-grpc: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc/tests + test-instrumentation-jinja2: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-jinja2/tests {posargs} + lint-instrumentation-jinja2: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-jinja2 + lint-instrumentation-jinja2: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-jinja2 + lint-instrumentation-jinja2: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-jinja2 + lint-instrumentation-jinja2: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-jinja2/src/opentelemetry + lint-instrumentation-jinja2: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-jinja2/tests + test-instrumentation-kafka-python: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-kafka-python/tests {posargs} + lint-instrumentation-kafka-python: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-kafka-python + lint-instrumentation-kafka-python: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-kafka-python + lint-instrumentation-kafka-python: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-kafka-python + lint-instrumentation-kafka-python: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-kafka-python/src/opentelemetry + lint-instrumentation-kafka-python: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-kafka-python/tests + test-instrumentation-confluent-kafka: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-confluent-kafka/tests {posargs} + lint-instrumentation-confluent-kafka: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-confluent-kafka + lint-instrumentation-confluent-kafka: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-confluent-kafka + lint-instrumentation-confluent-kafka: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-confluent-kafka + lint-instrumentation-confluent-kafka: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry + lint-instrumentation-confluent-kafka: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-confluent-kafka/tests + test-instrumentation-logging: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-logging/tests {posargs} + lint-instrumentation-logging: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-logging + lint-instrumentation-logging: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-logging + lint-instrumentation-logging: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-logging + lint-instrumentation-logging: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-logging/src/opentelemetry + lint-instrumentation-logging: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-logging/tests + test-instrumentation-mysql: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-mysql/tests {posargs} + lint-instrumentation-mysql: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-mysql + lint-instrumentation-mysql: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-mysql + lint-instrumentation-mysql: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-mysql + lint-instrumentation-mysql: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry + lint-instrumentation-mysql: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-mysql/tests + test-instrumentation-mysqlclient: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-mysqlclient/tests {posargs} + lint-instrumentation-mysqlclient: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-mysqlclient + lint-instrumentation-mysqlclient: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-mysqlclient + lint-instrumentation-mysqlclient: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-mysqlclient + lint-instrumentation-mysqlclient: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry + lint-instrumentation-mysqlclient: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-mysqlclient/tests + test-instrumentation-sio-pika: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/tests {posargs} + lint-instrumentation-sio-pika: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-pika + lint-instrumentation-sio-pika: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-pika + lint-instrumentation-sio-pika: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-pika + lint-instrumentation-sio-pika: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry + lint-instrumentation-sio-pika: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/tests + test-instrumentation-aio-pika: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/tests {posargs} + lint-instrumentation-aio-pika: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika + lint-instrumentation-aio-pika: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika + lint-instrumentation-aio-pika: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika + lint-instrumentation-aio-pika: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/src/opentelemetry + lint-instrumentation-aio-pika: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/tests + test-instrumentation-psycopg: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg/tests {posargs} + lint-instrumentation-psycopg: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg + lint-instrumentation-psycopg: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg + lint-instrumentation-psycopg: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg + lint-instrumentation-psycopg: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg/src/opentelemetry + lint-instrumentation-psycopg: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg/tests + test-instrumentation-psycopg2: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg2/tests {posargs} + lint-instrumentation-psycopg2: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg2 + lint-instrumentation-psycopg2: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg2 + lint-instrumentation-psycopg2: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg2 + lint-instrumentation-psycopg2: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry + lint-instrumentation-psycopg2: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg2/tests + test-instrumentation-pymemcache: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache/tests {posargs} + lint-instrumentation-pymemcache: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache + lint-instrumentation-pymemcache: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache + lint-instrumentation-pymemcache: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache + lint-instrumentation-pymemcache: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry + lint-instrumentation-pymemcache: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache/tests + test-instrumentation-pymongo: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-pymongo/tests {posargs} + lint-instrumentation-pymongo: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-pymongo + lint-instrumentation-pymongo: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-pymongo + lint-instrumentation-pymongo: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-pymongo + lint-instrumentation-pymongo: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry + lint-instrumentation-pymongo: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-pymongo/tests + test-instrumentation-pymysql: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-pymysql/tests {posargs} + lint-instrumentation-pymysql: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-pymysql + lint-instrumentation-pymysql: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-pymysql + lint-instrumentation-pymysql: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-pymysql + lint-instrumentation-pymysql: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry + lint-instrumentation-pymysql: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-pymysql/tests + test-instrumentation-pyramid: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-pyramid/tests {posargs} + lint-instrumentation-pyramid: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-pyramid + lint-instrumentation-pyramid: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-pyramid + lint-instrumentation-pyramid: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-pyramid + lint-instrumentation-pyramid: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry + lint-instrumentation-pyramid: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-pyramid/tests + test-instrumentation-redis: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-redis/tests {posargs} + lint-instrumentation-redis: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-redis + lint-instrumentation-redis: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-redis + lint-instrumentation-redis: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-redis + lint-instrumentation-redis: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry + lint-instrumentation-redis: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-redis/tests + test-instrumentation-remoulade: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-remoulade/tests {posargs} + lint-instrumentation-remoulade: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-remoulade + lint-instrumentation-remoulade: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-remoulade + lint-instrumentation-remoulade: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-remoulade + lint-instrumentation-remoulade: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-remoulade/src/opentelemetry + lint-instrumentation-remoulade: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-remoulade/tests + test-instrumentation-requests: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-requests/tests {posargs} + lint-instrumentation-requests: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-requests + lint-instrumentation-requests: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-requests + lint-instrumentation-requests: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-requests + lint-instrumentation-requests: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry + lint-instrumentation-requests: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-requests/tests + test-instrumentation-sklearn: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-sklearn/tests {posargs} + lint-instrumentation-sklearn: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-sklearn + lint-instrumentation-sklearn: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-sklearn + lint-instrumentation-sklearn: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-sklearn + lint-instrumentation-sklearn: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-sklearn/src/opentelemetry + lint-instrumentation-sklearn: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-sklearn/tests + test-instrumentation-sqlalchemy: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests {posargs} + lint-instrumentation-sqlalchemy: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy + lint-instrumentation-sqlalchemy: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy + lint-instrumentation-sqlalchemy: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy + lint-instrumentation-sqlalchemy: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry + lint-instrumentation-sqlalchemy: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests + test-instrumentation-sqlite3: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlite3/tests {posargs} + lint-instrumentation-sqlite3: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlite3 + lint-instrumentation-sqlite3: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlite3 + lint-instrumentation-sqlite3: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlite3 + lint-instrumentation-sqlite3: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlite3/src/opentelemetry + lint-instrumentation-sqlite3: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlite3/tests + test-instrumentation-starlette: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-starlette/tests {posargs} + lint-instrumentation-starlette: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-starlette + lint-instrumentation-starlette: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-starlette + lint-instrumentation-starlette: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-starlette + lint-instrumentation-starlette: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry + lint-instrumentation-starlette: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-starlette/tests + test-instrumentation-system-metrics: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-system-metrics/tests {posargs} + lint-instrumentation-system-metrics: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-system-metrics + lint-instrumentation-system-metrics: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-system-metrics + lint-instrumentation-system-metrics: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-system-metrics + lint-instrumentation-system-metrics: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry + lint-instrumentation-system-metrics: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-system-metrics/tests + test-instrumentation-threading: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-threading/tests {posargs} + lint-instrumentation-threading: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-threading + lint-instrumentation-threading: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-threading + lint-instrumentation-threading: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-threading + lint-instrumentation-threading: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry + lint-instrumentation-threading: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-threading/tests + test-instrumentation-tornado: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-tornado/tests {posargs} + lint-instrumentation-tornado: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-tornado + lint-instrumentation-tornado: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-tornado + lint-instrumentation-tornado: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-tornado + lint-instrumentation-tornado: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry + lint-instrumentation-tornado: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-tornado/tests + test-instrumentation-tortoiseorm: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-tortoiseorm/tests {posargs} + lint-instrumentation-tortoiseorm: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-tortoiseorm + lint-instrumentation-tortoiseorm: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-tortoiseorm + lint-instrumentation-tortoiseorm: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-tortoiseorm + lint-instrumentation-tortoiseorm: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-tortoiseorm/src/opentelemetry + lint-instrumentation-tortoiseorm: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-tortoiseorm/tests + test-instrumentation-wsgi: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi/tests {posargs} + lint-instrumentation-wsgi: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi + lint-instrumentation-wsgi: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi + lint-instrumentation-wsgi: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi + lint-instrumentation-wsgi: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry + lint-instrumentation-wsgi: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi/tests + test-instrumentation-httpx: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/tests {posargs} + lint-instrumentation-httpx: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx + lint-instrumentation-httpx: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx + lint-instrumentation-httpx: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx + lint-instrumentation-httpx: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry + lint-instrumentation-httpx: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/tests + test-instrumentation-asyncio: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncio/tests {posargs} + lint-instrumentation-asyncio: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncio + lint-instrumentation-asyncio: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncio + lint-instrumentation-asyncio: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncio + lint-instrumentation-asyncio: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry + lint-instrumentation-asyncio: pylint {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncio/tests + test-util-http: pytest {toxinidir}/util/opentelemetry-util-http/tests {posargs} + test-sdk-extension-aws: pytest {toxinidir}/sdk-extension/opentelemetry-sdk-extension-aws/tests {posargs} + lint-sdk-extension-aws: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/sdk-extension/opentelemetry-sdk-extension-aws + lint-sdk-extension-aws: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/sdk-extension/opentelemetry-sdk-extension-aws + lint-sdk-extension-aws: flake8 --config {toxinidir}/.flake8 {toxinidir}/sdk-extension/opentelemetry-sdk-extension-aws + lint-sdk-extension-aws: pylint {toxinidir}/sdk-extension/opentelemetry-sdk-extension-aws/src/opentelemetry + lint-sdk-extension-aws: pylint {toxinidir}/sdk-extension/opentelemetry-sdk-extension-aws/tests + test-resource-detector-container: pytest {toxinidir}/resource/opentelemetry-resource-detector-container/tests {posargs} + lint-resource-detector-container: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/resource/opentelemetry-resource-detector-container + lint-resource-detector-container: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/resource/opentelemetry-resource-detector-container + lint-resource-detector-container: flake8 --config {toxinidir}/.flake8 {toxinidir}/resource/opentelemetry-resource-detector-container + lint-resource-detector-container: pylint {toxinidir}/resource/opentelemetry-resource-detector-container/src/opentelemetry + lint-resource-detector-container: pylint {toxinidir}/resource/opentelemetry-resource-detector-container/tests + test-processor-baggage: pytest {toxinidir}/processor/opentelemetry-processor-baggage/tests {posargs} - test-propagator-aws: pytest {toxinidir}/propagator/opentelemetry-propagator-aws-xray/tests {posargs} + lint-processor-baggage: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/processor/opentelemetry-processor-baggage + lint-processor-baggage: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/processor/opentelemetry-processor-baggage + lint-processor-baggage: flake8 --config {toxinidir}/.flake8 {toxinidir}/processor/opentelemetry-processor-baggage + lint-processor-baggage: pylint {toxinidir}/processor/opentelemetry-processor-baggage/src/opentelemetry + lint-processor-baggage: pylint {toxinidir}/processor/opentelemetry-processor-baggage/tests + + test-propagator-aws-xray: pytest {toxinidir}/propagator/opentelemetry-propagator-aws-xray/tests {posargs} + lint-propagator-aws-xray: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/propagator/opentelemetry-propagator-aws-xray + lint-propagator-aws-xray: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/propagator/opentelemetry-propagator-aws-xray + lint-propagator-aws-xray: flake8 --config {toxinidir}/.flake8 {toxinidir}/propagator/opentelemetry-propagator-aws-xray + lint-propagator-aws-xray: pylint {toxinidir}/propagator/opentelemetry-propagator-aws-xray/src/opentelemetry + lint-propagator-aws-xray: pylint {toxinidir}/propagator/opentelemetry-propagator-aws-xray/tests + test-propagator-ot-trace: pytest {toxinidir}/propagator/opentelemetry-propagator-ot-trace/tests {posargs} + lint-propagator-ot-trace: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/propagator/opentelemetry-propagator-ot-trace + lint-propagator-ot-trace: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/propagator/opentelemetry-propagator-ot-trace + lint-propagator-ot-trace: flake8 --config {toxinidir}/.flake8 {toxinidir}/propagator/opentelemetry-propagator-ot-trace + lint-propagator-ot-trace: pylint {toxinidir}/propagator/opentelemetry-propagator-ot-trace/src/opentelemetry + lint-propagator-ot-trace: pylint {toxinidir}/propagator/opentelemetry-propagator-ot-trace/tests + test-exporter-richconsole: pytest {toxinidir}/exporter/opentelemetry-exporter-richconsole/tests {posargs} + lint-exporter-richconsole: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/exporter/opentelemetry-exporter-richconsole + lint-exporter-richconsole: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/exporter/opentelemetry-exporter-richconsole + lint-exporter-richconsole: flake8 --config {toxinidir}/.flake8 {toxinidir}/exporter/opentelemetry-exporter-richconsole + lint-exporter-richconsole: pylint {toxinidir}/exporter/opentelemetry-exporter-richconsole/src/opentelemetry + lint-exporter-richconsole: pylint {toxinidir}/exporter/opentelemetry-exporter-richconsole/tests + test-exporter-prometheus-remote-write: pytest {toxinidir}/exporter/opentelemetry-exporter-prometheus-remote-write/tests {posargs} + lint-exporter-prometheus-remote-write: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/exporter/opentelemetry-exporter-prometheus-remote-write + lint-exporter-prometheus-remote-write: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/exporter/opentelemetry-exporter-prometheus-remote-write + lint-exporter-prometheus-remote-write: flake8 --config {toxinidir}/.flake8 {toxinidir}/exporter/opentelemetry-exporter-prometheus-remote-write + lint-exporter-prometheus-remote-write: pylint {toxinidir}/exporter/opentelemetry-exporter-prometheus-remote-write/src/opentelemetry + lint-exporter-prometheus-remote-write: pylint {toxinidir}/exporter/opentelemetry-exporter-prometheus-remote-write/tests + coverage: {toxinidir}/scripts/coverage.sh [testenv:docs] @@ -782,81 +1198,6 @@ deps = commands = codespell -[testenv:lint] -basepython: python3 -recreate = True -deps = - -r dev-requirements.txt - -commands_pre = - python -m pip install {env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api - python -m pip install {env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions - python -m pip install {env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk - python -m pip install {env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils - python -m pip install -e {toxinidir}/util/opentelemetry-util-http[test] - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/test-requirements.txt - pip install -r {toxinidir}/opentelemetry-instrumentation/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-3.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-starlette/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-2.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-flask/test-requirements-2.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-1.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-cassandra/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/test-requirements-1.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-boto/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-boto3sqs/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-2.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-redis/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-fastapi/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-jinja2/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-kafka-python/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-remoulade/test-requirements.txt - ; pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-sklearn/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-4.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-client/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-server/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg2/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-1.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pyramid/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-requests/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-1.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlite3/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-confluent-kafka/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymysql/test-requirements.txt - # prerequisite: follow the instructions here https://github.com/PyMySQL/mysqlclient#install - # for your OS to install the required dependencies - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-mysqlclient/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-tornado/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymongo/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aws-lambda/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-mysql/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncio/test-requirements.txt - pip install -r {toxinidir}/exporter/opentelemetry-exporter-richconsole/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-system-metrics/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-threading/test-requirements.txt - pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-tortoiseorm/test-requirements.txt - # requires snappy headers to be available on the system - pip install -r {toxinidir}/resource/opentelemetry-resource-detector-container/test-requirements.txt - pip install -r {toxinidir}/processor/opentelemetry-processor-baggage/test-requirements.txt - pip install -r {toxinidir}/propagator/opentelemetry-propagator-aws-xray/test-requirements.txt - pip install -r {toxinidir}/propagator/opentelemetry-propagator-ot-trace/test-requirements.txt - pip install -r {toxinidir}/sdk-extension/opentelemetry-sdk-extension-aws/test-requirements.txt - pip install -r {toxinidir}/opentelemetry-distro/test-requirements.txt - -commands = - python scripts/eachdist.py lint --check-only - [testenv:docker-tests] basepython: python3 deps = From 2a174b25437ac7bbe763d790d231ecd04bbe2168 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Thu, 2 May 2024 19:11:02 +0200 Subject: [PATCH 32/58] CONTRIBUTING: introduce pre-commit (#2479) --- .pre-commit-config.yaml | 14 ++++++++++++++ CONTRIBUTING.md | 11 +++++++++++ dev-requirements.txt | 2 ++ 3 files changed, 27 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..cbdffe129b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,14 @@ +repos: + - repo: https://github.com/psf/black-pre-commit-mirror + rev: 24.3.0 + hooks: + - id: black + language_version: python3.11 + - repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + - repo: https://github.com/pycqa/flake8 + rev: '6.1.0' + hooks: + - id: flake8 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3c4bae0f47..3de25a4e67 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -70,6 +70,17 @@ An easier way to do so is: 1. Run `.tox/lint-some-package/bin/black .` 2. Run `.tox/lint-some-package/bin/isort .` +Or you can call formatting and linting in one command by [pre-commit](https://pre-commit.com/): + +```console +$ pre-commit +``` + +You can also configure it to run lint tools automatically before committing with: + +```console +$ pre-commit install + See [`tox.ini`](https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/tox.ini) for more detail on available tox commands. diff --git a/dev-requirements.txt b/dev-requirements.txt index 1c49c57b7e..be65b731c7 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -17,3 +17,5 @@ codespell==2.1.0 requests==2.31.0 ruamel.yaml==0.17.21 flaky==3.7.0 +pre-commit==3.7.0; python_version >= '3.9' +pre-commit==3.5.0; python_version < '3.9' From 1ee7261ea7117fbd22e2262e488402213a874125 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 2 May 2024 18:05:49 -0500 Subject: [PATCH 33/58] Refactor bootstrap generation (#2101) * Refactor bootstrap generation This makes the bootstrap script get the package version directly from pypi instead of from our lists of packages. This makes sure that the packages are actually available for the end user to install. Fixes #2053 * Fix lint * Fix lint * Remove aiohttp * Add missing dependency for aiohttp-client * Use hatch version --- .../instrumentation/bootstrap_gen.py | 5 -- scripts/otel_packaging.py | 46 ++++++++++++------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py index 9eebd5bb38..55d2f498a1 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py @@ -24,10 +24,6 @@ "library": "aiohttp ~= 3.0", "instrumentation": "opentelemetry-instrumentation-aiohttp-client==0.46b0.dev", }, - { - "library": "aiohttp ~= 3.0", - "instrumentation": "opentelemetry-instrumentation-aiohttp-server==0.46b0.dev", - }, { "library": "aiopg >= 0.13.0, < 2.0.0", "instrumentation": "opentelemetry-instrumentation-aiopg==0.46b0.dev", @@ -191,7 +187,6 @@ "opentelemetry-instrumentation-dbapi==0.46b0.dev", "opentelemetry-instrumentation-logging==0.46b0.dev", "opentelemetry-instrumentation-sqlite3==0.46b0.dev", - "opentelemetry-instrumentation-threading==0.46b0.dev", "opentelemetry-instrumentation-urllib==0.46b0.dev", "opentelemetry-instrumentation-wsgi==0.46b0.dev", ] diff --git a/scripts/otel_packaging.py b/scripts/otel_packaging.py index 2f42e44189..c6c11c45fa 100644 --- a/scripts/otel_packaging.py +++ b/scripts/otel_packaging.py @@ -12,43 +12,55 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os -import subprocess -from subprocess import CalledProcessError +from tomli import load +from os import path, listdir +from subprocess import check_output, CalledProcessError +from requests import get -import tomli - -scripts_path = os.path.dirname(os.path.abspath(__file__)) -root_path = os.path.dirname(scripts_path) -instrumentations_path = os.path.join(root_path, "instrumentation") +scripts_path = path.dirname(path.abspath(__file__)) +root_path = path.dirname(scripts_path) +instrumentations_path = path.join(root_path, "instrumentation") def get_instrumentation_packages(): - for pkg in sorted(os.listdir(instrumentations_path)): - pkg_path = os.path.join(instrumentations_path, pkg) - if not os.path.isdir(pkg_path): + for pkg in sorted(listdir(instrumentations_path)): + pkg_path = path.join(instrumentations_path, pkg) + if not path.isdir(pkg_path): continue + error = f"Could not get version for package {pkg}" + try: - version = subprocess.check_output( + hatch_version = check_output( "hatch version", shell=True, cwd=pkg_path, - universal_newlines=True, + universal_newlines=True ) + except CalledProcessError as exc: print(f"Could not get hatch version from path {pkg_path}") print(exc.output) - raise exc - pyproject_toml_path = os.path.join(pkg_path, "pyproject.toml") + try: + response = get(f"https://pypi.org/pypi/{pkg}/json", timeout=10) + + except Exception: + print(error) + continue + + if response.status_code != 200: + print(error) + continue + + pyproject_toml_path = path.join(pkg_path, "pyproject.toml") with open(pyproject_toml_path, "rb") as file: - pyproject_toml = tomli.load(file) + pyproject_toml = load(file) instrumentation = { "name": pyproject_toml["project"]["name"], - "version": version.strip(), + "version": hatch_version.strip(), "instruments": pyproject_toml["project"]["optional-dependencies"][ "instruments" ], From 1d3dea0475af94399f6b251c67712272831d40ae Mon Sep 17 00:00:00 2001 From: Aaron Abbott Date: Sat, 4 May 2024 16:47:19 -0400 Subject: [PATCH 34/58] Remove SDK dependency from opentelemetry-instrumentation-grpc (#2474) Co-authored-by: Diego Hurtado Co-authored-by: Riccardo Magliocchetti --- CHANGELOG.md | 2 ++ .../opentelemetry-instrumentation-grpc/pyproject.toml | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ceb97433f1..dff32bafca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2418](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2418)) - Use sqlalchemy version in sqlalchemy commenter instead of opentelemetry library version ([#2404](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2404)) +- Remove SDK dependency from opentelemetry-instrumentation-grpc + ([#2474](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2474)) ## Version 1.24.0/0.45b0 (2024-03-28) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/pyproject.toml b/instrumentation/opentelemetry-instrumentation-grpc/pyproject.toml index 750f76270a..7deffb71e7 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-grpc/pyproject.toml @@ -26,7 +26,6 @@ classifiers = [ dependencies = [ "opentelemetry-api ~= 1.12", "opentelemetry-instrumentation == 0.46b0.dev", - "opentelemetry-sdk ~= 1.12", "opentelemetry-semantic-conventions == 0.46b0.dev", "wrapt >= 1.0.0, < 2.0.0", ] From 0a231e57f9722e6101194c6b38695addf23ab950 Mon Sep 17 00:00:00 2001 From: Akshay Awate Date: Mon, 6 May 2024 22:21:39 +0530 Subject: [PATCH 35/58] Update README.rst (#2499) * Update README.rst Fix README hyperlink syntax. * updated README --- opentelemetry-contrib-instrumentations/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-contrib-instrumentations/README.rst b/opentelemetry-contrib-instrumentations/README.rst index 4e581c0a63..e0a76806e1 100644 --- a/opentelemetry-contrib-instrumentations/README.rst +++ b/opentelemetry-contrib-instrumentations/README.rst @@ -15,7 +15,7 @@ Installation This package installs all instrumentation packages hosted by the OpenTelemetry Python Contrib repository. -The list of packages can be found (here)[https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation] +The list of packages can be found `here `_. References From bc804a3b07b7af45bbb352d553591d3a72845376 Mon Sep 17 00:00:00 2001 From: Allen Kim Date: Wed, 8 May 2024 08:40:21 +0900 Subject: [PATCH 36/58] Bugfix/check future cancelled (#2461) * Calling the exception() method when future is in the cancelled state is causing a CancelledError Calling the exception() method when future is in the cancelled state is causing a CancelledError. we should check the cancelled state first and call f.exception() only if it's not cancelled. * modify lint * modify lint * Update CHANGELOG.md * remove init() * add future cancelled test code * add future cancelled test code * add future cancelled test code * add future cancelled test code * add future cancelled test code * add future cancelled test code * lint * lint * remove if condition * modify test code * lint * lint * remove pytest --------- Co-authored-by: Diego Hurtado --- CHANGELOG.md | 2 + .../instrumentation/asyncio/__init__.py | 24 +++----- .../tests/test_asyncio_future_cancellation.py | 60 +++++++++++++++++++ 3 files changed, 71 insertions(+), 15 deletions(-) create mode 100644 instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_future_cancellation.py diff --git a/CHANGELOG.md b/CHANGELOG.md index dff32bafca..28e8a85c26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2418](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2418)) - Use sqlalchemy version in sqlalchemy commenter instead of opentelemetry library version ([#2404](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2404)) +- `opentelemetry-instrumentation-asyncio` Check for cancelledException in the future + ([#2461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2461)) - Remove SDK dependency from opentelemetry-instrumentation-grpc ([#2474](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2474)) diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py b/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py index 68e3d0839f..72aa5fd2aa 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py @@ -116,21 +116,11 @@ class AsyncioInstrumentor(BaseInstrumentor): "run_coroutine_threadsafe", ] - def __init__(self): - super().__init__() - self.process_duration_histogram = None - self.process_created_counter = None - - self._tracer = None - self._meter = None - self._coros_name_to_trace: set = set() - self._to_thread_name_to_trace: set = set() - self._future_active_enabled: bool = False - def instrumentation_dependencies(self) -> Collection[str]: return _instruments def _instrument(self, **kwargs): + # pylint: disable=attribute-defined-outside-init self._tracer = get_tracer( __name__, __version__, kwargs.get("tracer_provider") ) @@ -307,13 +297,17 @@ def trace_future(self, future): ) def callback(f): - exception = f.exception() attr = { "type": "future", + "state": ( + "cancelled" + if f.cancelled() + else determine_state(f.exception()) + ), } - state = determine_state(exception) - attr["state"] = state - self.record_process(start, attr, span, exception) + self.record_process( + start, attr, span, None if f.cancelled() else f.exception() + ) future.add_done_callback(callback) return future diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_future_cancellation.py b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_future_cancellation.py new file mode 100644 index 0000000000..f8f4e5f230 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_future_cancellation.py @@ -0,0 +1,60 @@ +import asyncio +from unittest.mock import patch + +from opentelemetry.instrumentation.asyncio import AsyncioInstrumentor +from opentelemetry.instrumentation.asyncio.environment_variables import ( + OTEL_PYTHON_ASYNCIO_FUTURE_TRACE_ENABLED, +) +from opentelemetry.test.test_base import TestBase +from opentelemetry.trace import get_tracer + + +class TestTraceFuture(TestBase): + @patch.dict( + "os.environ", {OTEL_PYTHON_ASYNCIO_FUTURE_TRACE_ENABLED: "true"} + ) + def setUp(self): + super().setUp() + self._tracer = get_tracer( + __name__, + ) + self.instrumentor = AsyncioInstrumentor() + self.instrumentor.instrument() + + def tearDown(self): + super().tearDown() + self.instrumentor.uninstrument() + + def test_trace_future_cancelled(self): + async def future_cancelled(): + with self._tracer.start_as_current_span("root"): + future = asyncio.Future() + future = self.instrumentor.trace_future(future) + future.cancel() + + try: + asyncio.run(future_cancelled()) + except asyncio.CancelledError as exc: + self.assertEqual(isinstance(exc, asyncio.CancelledError), True) + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 2) + self.assertEqual(spans[0].name, "root") + self.assertEqual(spans[1].name, "asyncio future") + + metrics = ( + self.memory_metrics_reader.get_metrics_data() + .resource_metrics[0] + .scope_metrics[0] + .metrics + ) + self.assertEqual(len(metrics), 2) + + self.assertEqual(metrics[0].name, "asyncio.process.duration") + self.assertEqual( + metrics[0].data.data_points[0].attributes["state"], "cancelled" + ) + + self.assertEqual(metrics[1].name, "asyncio.process.created") + self.assertEqual( + metrics[1].data.data_points[0].attributes["state"], "cancelled" + ) From 935f51eb8eaa54fc7d2c7dd6718906fe782cf223 Mon Sep 17 00:00:00 2001 From: hyfj44255 Date: Thu, 9 May 2024 23:14:08 +0800 Subject: [PATCH 37/58] upgrade pymongo to avoid CWE-125 vulnerability issue (#2497) Signed-off-by: Yang, Robin --- .../opentelemetry-instrumentation-pymongo/test-requirements.txt | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-pymongo/test-requirements.txt index 01d48e8dc4..0ad6375a14 100644 --- a/instrumentation/opentelemetry-instrumentation-pymongo/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-pymongo/test-requirements.txt @@ -8,7 +8,7 @@ packaging==23.2 pluggy==1.4.0 py==1.11.0 py-cpuinfo==9.0.0 -pymongo==4.6.2 +pymongo==4.6.3 pytest==7.1.3 pytest-benchmark==4.0.0 tomli==2.0.1 diff --git a/tox.ini b/tox.ini index ed74e485cd..37a1727935 100644 --- a/tox.ini +++ b/tox.ini @@ -1250,7 +1250,7 @@ deps = psycopg2==2.9.9 psycopg2-binary==2.9.9 pycparser==2.21 - pymongo==4.6.2 + pymongo==4.6.3 PyMySQL==0.10.1 PyNaCl==1.5.0 # prerequisite: install unixodbc From eabceff062372d3dd17d2dc790d34919f0c94b5f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 May 2024 13:53:12 -0500 Subject: [PATCH 38/58] Bump jinja2 from 3.1.3 to 3.1.4 (#2503) Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.3 to 3.1.4. - [Release notes](https://github.com/pallets/jinja/releases) - [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/jinja/compare/3.1.3...3.1.4) --- updated-dependencies: - dependency-name: jinja2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Diego Hurtado --- gen-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gen-requirements.txt b/gen-requirements.txt index de84b72c1e..b2d5c4f695 100644 --- a/gen-requirements.txt +++ b/gen-requirements.txt @@ -1,6 +1,6 @@ -c dev-requirements.txt astor==0.8.1 -jinja2==3.1.3 +jinja2==3.1.4 markupsafe==2.0.1 isort black From 46d2ce6acea9a1a6cb1a4d4c863077002f5f7d21 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Fri, 10 May 2024 18:51:32 +0200 Subject: [PATCH 39/58] Reinstate tox -e lint (#2482) --- CONTRIBUTING.md | 4 ++-- tox.ini | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3de25a4e67..743449c2a7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -67,8 +67,8 @@ You can run `tox` with the following arguments: `black` and `isort` are executed when `tox -e lint` is run. The reported errors can be tedious to fix manually. An easier way to do so is: -1. Run `.tox/lint-some-package/bin/black .` -2. Run `.tox/lint-some-package/bin/isort .` +1. Run `.tox/lint/bin/black .` +2. Run `.tox/lint/bin/isort .` Or you can call formatting and linting in one command by [pre-commit](https://pre-commit.com/): diff --git a/tox.ini b/tox.ini index 37a1727935..ae11e0f24f 100644 --- a/tox.ini +++ b/tox.ini @@ -1189,6 +1189,17 @@ changedir = docs commands = sphinx-build -E -a -W -b html -T . _build/html +[testenv:lint] +basepython: python3 +recreate = True +deps = + -r dev-requirements.txt + +commands = + black --config {toxinidir}/pyproject.toml {{toxinidir}} --diff --check + isort --settings-path {toxinidir}/.isort.cfg {{toxinidir}} --diff --check-only + flake8 --config {toxinidir}/.flake8 {toxinidir} + [testenv:spellcheck] basepython: python3 recreate = True From 9b7197d3b989dac0a7965b6c55b2ee902b3f5b45 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Tue, 14 May 2024 03:24:24 +1000 Subject: [PATCH 40/58] docs: fix name of response hook signature in botocore instrumentation (#2512) --- .../src/opentelemetry/instrumentation/botocore/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py index 36b973e318..0481b248aa 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py @@ -51,7 +51,7 @@ request_hook (Callable) - a function with extra user-defined logic to be performed before performing the request this function signature is: def request_hook(span: Span, service_name: str, operation_name: str, api_params: dict) -> None response_hook (Callable) - a function with extra user-defined logic to be performed after performing the request -this function signature is: def request_hook(span: Span, service_name: str, operation_name: str, result: dict) -> None +this function signature is: def response_hook(span: Span, service_name: str, operation_name: str, result: dict) -> None for example: From 6a40ffd90512e3e4636bddb20728f8f680b69f8a Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 14 May 2024 21:59:41 +0200 Subject: [PATCH 41/58] elasticsearch: tests against elasticsearch 8 (#2420) * elasticsearch: bump handled version to 6.0 After 4de0e5659d451baee65af412242b95f174444d87 * elasticsearch: tests against elasticsearch 8 --- CHANGELOG.md | 2 + instrumentation/README.md | 2 +- .../instrumentation/elasticsearch/__init__.py | 61 ++++++++-- .../instrumentation/elasticsearch/package.py | 2 +- .../test-requirements-2.txt | 23 ++++ .../tests/helpers_es6.py | 6 + .../tests/helpers_es7.py | 6 + .../tests/helpers_es8.py | 21 +++- .../tests/test_elasticsearch.py | 112 ++++++++++++------ tox.ini | 8 +- 10 files changed, 191 insertions(+), 52 deletions(-) create mode 100644 instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 28e8a85c26..d10983c10b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2461)) - Remove SDK dependency from opentelemetry-instrumentation-grpc ([#2474](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2474)) +- `opentelemetry-instrumentation-elasticsearch` Improved support for version 8 + ([#2420](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2420)) ## Version 1.24.0/0.45b0 (2024-03-28) diff --git a/instrumentation/README.md b/instrumentation/README.md index c73d0f7c0a..5dfed03e9a 100644 --- a/instrumentation/README.md +++ b/instrumentation/README.md @@ -17,7 +17,7 @@ | [opentelemetry-instrumentation-confluent-kafka](./opentelemetry-instrumentation-confluent-kafka) | confluent-kafka >= 1.8.2, <= 2.3.0 | No | experimental | [opentelemetry-instrumentation-dbapi](./opentelemetry-instrumentation-dbapi) | dbapi | No | experimental | [opentelemetry-instrumentation-django](./opentelemetry-instrumentation-django) | django >= 1.10 | Yes | experimental -| [opentelemetry-instrumentation-elasticsearch](./opentelemetry-instrumentation-elasticsearch) | elasticsearch >= 2.0 | No | experimental +| [opentelemetry-instrumentation-elasticsearch](./opentelemetry-instrumentation-elasticsearch) | elasticsearch >= 6.0 | No | experimental | [opentelemetry-instrumentation-falcon](./opentelemetry-instrumentation-falcon) | falcon >= 1.4.1, < 4.0.0 | Yes | experimental | [opentelemetry-instrumentation-fastapi](./opentelemetry-instrumentation-fastapi) | fastapi ~= 0.58 | Yes | experimental | [opentelemetry-instrumentation-flask](./opentelemetry-instrumentation-flask) | flask >= 1.0 | Yes | migration diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py index ceb50cac56..acf4596fb0 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py @@ -94,7 +94,7 @@ def response_hook(span, response): from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.utils import unwrap from opentelemetry.semconv.trace import SpanAttributes -from opentelemetry.trace import SpanKind, get_tracer +from opentelemetry.trace import SpanKind, Status, StatusCode, get_tracer from .utils import sanitize_body @@ -103,6 +103,7 @@ def response_hook(span, response): es_transport_split = elasticsearch.VERSION[0] > 7 if es_transport_split: import elastic_transport + from elastic_transport._models import DefaultType logger = getLogger(__name__) @@ -173,7 +174,12 @@ def _instrument(self, **kwargs): def _uninstrument(self, **kwargs): # pylint: disable=no-member - unwrap(elasticsearch.Transport, "perform_request") + transport_class = ( + elastic_transport.Transport + if es_transport_split + else elasticsearch.Transport + ) + unwrap(transport_class, "perform_request") _regex_doc_url = re.compile(r"/_doc/([^/]+)") @@ -182,6 +188,7 @@ def _uninstrument(self, **kwargs): _regex_search_url = re.compile(r"/([^/]+)/_search[/]?") +# pylint: disable=too-many-statements def _wrap_perform_request( tracer, span_name_prefix, @@ -234,7 +241,22 @@ def wrapper(wrapped, _, args, kwargs): kind=SpanKind.CLIENT, ) as span: if callable(request_hook): - request_hook(span, method, url, kwargs) + # elasticsearch 8 changed the parameters quite a bit + if es_transport_split: + + def normalize_kwargs(k, v): + if isinstance(v, DefaultType): + v = str(v) + elif isinstance(v, elastic_transport.HttpHeaders): + v = dict(v) + return (k, v) + + hook_kwargs = dict( + normalize_kwargs(k, v) for k, v in kwargs.items() + ) + else: + hook_kwargs = kwargs + request_hook(span, method, url, hook_kwargs) if span.is_recording(): attributes = { @@ -260,16 +282,41 @@ def wrapper(wrapped, _, args, kwargs): span.set_attribute(key, value) rv = wrapped(*args, **kwargs) - if isinstance(rv, dict) and span.is_recording(): + + body = rv.body if es_transport_split else rv + if isinstance(body, dict) and span.is_recording(): for member in _ATTRIBUTES_FROM_RESULT: - if member in rv: + if member in body: span.set_attribute( f"elasticsearch.{member}", - str(rv[member]), + str(body[member]), + ) + + # since the transport split the raising of exceptions that set the error status + # are called after this code so need to set error status manually + if es_transport_split and span.is_recording(): + if not (method == "HEAD" and rv.meta.status == 404) and ( + not 200 <= rv.meta.status < 299 + ): + exception = elasticsearch.exceptions.HTTP_EXCEPTIONS.get( + rv.meta.status, elasticsearch.exceptions.ApiError + ) + message = str(body) + if isinstance(body, dict): + error = body.get("error", message) + if isinstance(error, dict) and "type" in error: + error = error["type"] + message = error + + span.set_status( + Status( + status_code=StatusCode.ERROR, + description=f"{exception.__name__}: {message}", ) + ) if callable(response_hook): - response_hook(span, rv) + response_hook(span, body) return rv return wrapper diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/package.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/package.py index 5b0fb7e6ea..bae644a70b 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/package.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/package.py @@ -13,4 +13,4 @@ # limitations under the License. -_instruments = ("elasticsearch >= 2.0",) +_instruments = ("elasticsearch >= 6.0",) diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt new file mode 100644 index 0000000000..23d87f93dd --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt @@ -0,0 +1,23 @@ +asgiref==3.7.2 +attrs==23.2.0 +Deprecated==1.2.14 +elasticsearch==8.12.1 +elasticsearch-dsl==8.12.0 +elastic-transport==8.12.0 +importlib-metadata==6.11.0 +iniconfig==2.0.0 +packaging==23.2 +pluggy==1.4.0 +py==1.11.0 +py-cpuinfo==9.0.0 +pytest==7.1.3 +pytest-benchmark==4.0.0 +python-dateutil==2.8.2 +six==1.16.0 +tomli==2.0.1 +typing_extensions==4.10.0 +urllib3==2.2.1 +wrapt==1.16.0 +zipp==3.17.0 +-e opentelemetry-instrumentation +-e instrumentation/opentelemetry-instrumentation-elasticsearch diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es6.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es6.py index b27d291ba3..8169eb25c4 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es6.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es6.py @@ -31,3 +31,9 @@ class Index: dsl_index_span_name = "Elasticsearch/test-index/doc/2" dsl_index_url = "/test-index/doc/2" dsl_search_method = "GET" + +perform_request_mock_path = "elasticsearch.connection.http_urllib3.Urllib3HttpConnection.perform_request" + + +def mock_response(body: str, status_code: int = 200): + return (status_code, {}, body) diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es7.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es7.py index b22df18452..377173f7ac 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es7.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es7.py @@ -29,3 +29,9 @@ class Index: dsl_index_span_name = "Elasticsearch/test-index/_doc/:id" dsl_index_url = "/test-index/_doc/2" dsl_search_method = "POST" + +perform_request_mock_path = "elasticsearch.connection.http_urllib3.Urllib3HttpConnection.perform_request" + + +def mock_response(body: str, status_code: int = 200): + return (status_code, {}, body) diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es8.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es8.py index 04ed2efda2..a450be68ec 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es8.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es8.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from elastic_transport import ApiResponseMeta, HttpHeaders +from elastic_transport._node import NodeApiResponse from elasticsearch_dsl import Document, Keyword, Text @@ -36,6 +38,23 @@ class Index: } } dsl_index_result = (1, {}, '{"result": "created"}') -dsl_index_span_name = "Elasticsearch/test-index/_doc/2" +dsl_index_span_name = "Elasticsearch/test-index/_doc/:id" dsl_index_url = "/test-index/_doc/2" dsl_search_method = "POST" + +perform_request_mock_path = ( + "elastic_transport._node._http_urllib3.Urllib3HttpNode.perform_request" +) + + +def mock_response(body: str, status_code: int = 200): + return NodeApiResponse( + ApiResponseMeta( + status=status_code, + headers=HttpHeaders({}), + duration=100, + http_version="1.1", + node="node", + ), + body.encode(), + ) diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py index 690cbe3d4c..b0ee170329 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py @@ -51,25 +51,25 @@ def normalize_arguments(doc_type, body=None): - if major_version == 7: - return {"document": body} if body else {} - return ( - {"body": body, "doc_type": doc_type} - if body - else {"doc_type": doc_type} - ) + if major_version < 7: + return ( + {"body": body, "doc_type": doc_type} + if body + else {"doc_type": doc_type} + ) + return {"document": body} if body else {} def get_elasticsearch_client(*args, **kwargs): client = Elasticsearch(*args, **kwargs) - if major_version == 7: + if major_version == 8: + client._verified_elasticsearch = True + elif major_version == 7: client.transport._verified_elasticsearch = True return client -@mock.patch( - "elasticsearch.connection.http_urllib3.Urllib3HttpConnection.perform_request" -) +@mock.patch(helpers.perform_request_mock_path) class TestElasticsearchIntegration(TestBase): search_attributes = { SpanAttributes.DB_SYSTEM: "elasticsearch", @@ -96,7 +96,7 @@ def tearDown(self): ElasticsearchInstrumentor().uninstrument() def test_instrumentor(self, request_mock): - request_mock.return_value = (1, {}, "{}") + request_mock.return_value = helpers.mock_response("{}") es = get_elasticsearch_client(hosts=["http://localhost:9200"]) es.index( @@ -147,7 +147,7 @@ def test_prefix_arg(self, request_mock): prefix = "prefix-from-env" ElasticsearchInstrumentor().uninstrument() ElasticsearchInstrumentor(span_name_prefix=prefix).instrument() - request_mock.return_value = (1, {}, "{}") + request_mock.return_value = helpers.mock_response("{}") self._test_prefix(prefix) def test_prefix_env(self, request_mock): @@ -156,7 +156,7 @@ def test_prefix_env(self, request_mock): os.environ[env_var] = prefix ElasticsearchInstrumentor().uninstrument() ElasticsearchInstrumentor().instrument() - request_mock.return_value = (1, {}, "{}") + request_mock.return_value = helpers.mock_response("{}") del os.environ[env_var] self._test_prefix(prefix) @@ -174,10 +174,8 @@ def _test_prefix(self, prefix): self.assertTrue(span.name.startswith(prefix)) def test_result_values(self, request_mock): - request_mock.return_value = ( - 1, - {}, - '{"found": false, "timed_out": true, "took": 7}', + request_mock.return_value = helpers.mock_response( + '{"found": false, "timed_out": true, "took": 7}' ) es = get_elasticsearch_client(hosts=["http://localhost:9200"]) es.get( @@ -201,9 +199,18 @@ def test_trace_error_unknown(self, request_mock): def test_trace_error_not_found(self, request_mock): msg = "record not found" - exc = elasticsearch.exceptions.NotFoundError(404, msg) - request_mock.return_value = (1, {}, "{}") - request_mock.side_effect = exc + if major_version == 8: + error = {"error": msg} + response = helpers.mock_response( + json.dumps(error), status_code=404 + ) + request_mock.return_value = response + exc = elasticsearch.exceptions.NotFoundError( + msg, meta=response.meta, body=None + ) + else: + exc = elasticsearch.exceptions.NotFoundError(404, msg) + request_mock.side_effect = exc self._test_trace_error(StatusCode.ERROR, exc) def _test_trace_error(self, code, exc): @@ -222,12 +229,13 @@ def _test_trace_error(self, code, exc): span = spans[0] self.assertFalse(span.status.is_ok) self.assertEqual(span.status.status_code, code) + message = getattr(exc, "message", str(exc)) self.assertEqual( - span.status.description, f"{type(exc).__name__}: {exc}" + span.status.description, f"{type(exc).__name__}: {message}" ) def test_parent(self, request_mock): - request_mock.return_value = (1, {}, "{}") + request_mock.return_value = helpers.mock_response("{}") es = get_elasticsearch_client(hosts=["http://localhost:9200"]) with self.tracer.start_as_current_span("parent"): es.index( @@ -245,7 +253,7 @@ def test_parent(self, request_mock): self.assertEqual(child.parent.span_id, parent.context.span_id) def test_multithread(self, request_mock): - request_mock.return_value = (1, {}, "{}") + request_mock.return_value = helpers.mock_response("{}") es = get_elasticsearch_client(hosts=["http://localhost:9200"]) ev = threading.Event() @@ -292,7 +300,9 @@ def target2(): self.assertIsNone(s3.parent) def test_dsl_search(self, request_mock): - request_mock.return_value = (1, {}, '{"hits": {"hits": []}}') + request_mock.return_value = helpers.mock_response( + '{"hits": {"hits": []}}' + ) client = get_elasticsearch_client(hosts=["http://localhost:9200"]) search = Search(using=client, index="test-index").filter( @@ -310,7 +320,9 @@ def test_dsl_search(self, request_mock): ) def test_dsl_search_sanitized(self, request_mock): - request_mock.return_value = (1, {}, '{"hits": {"hits": []}}') + request_mock.return_value = helpers.mock_response( + '{"hits": {"hits": []}}' + ) client = get_elasticsearch_client(hosts=["http://localhost:9200"]) search = Search(using=client, index="test-index").filter( "term", author="testing" @@ -327,7 +339,10 @@ def test_dsl_search_sanitized(self, request_mock): ) def test_dsl_create(self, request_mock): - request_mock.return_value = (1, {}, "{}") + request_mock.side_effect = [ + helpers.mock_response("{}", status_code=404), + helpers.mock_response("{}"), + ] client = get_elasticsearch_client(hosts=["http://localhost:9200"]) Article.init(using=client) @@ -354,7 +369,10 @@ def test_dsl_create(self, request_mock): ) def test_dsl_create_sanitized(self, request_mock): - request_mock.return_value = (1, {}, "{}") + request_mock.side_effect = [ + helpers.mock_response("{}", status_code=404), + helpers.mock_response("{}"), + ] client = get_elasticsearch_client(hosts=["http://localhost:9200"]) Article.init(using=client) @@ -370,7 +388,9 @@ def test_dsl_create_sanitized(self, request_mock): ) def test_dsl_index(self, request_mock): - request_mock.return_value = (1, {}, helpers.dsl_index_result[2]) + request_mock.return_value = helpers.mock_response( + helpers.dsl_index_result[2] + ) client = get_elasticsearch_client(hosts=["http://localhost:9200"]) article = Article( @@ -416,10 +436,8 @@ def request_hook(span, method, url, kwargs): ElasticsearchInstrumentor().uninstrument() ElasticsearchInstrumentor().instrument(request_hook=request_hook) - request_mock.return_value = ( - 1, - {}, - '{"found": false, "timed_out": true, "took": 7}', + request_mock.return_value = helpers.mock_response( + '{"found": false, "timed_out": true, "took": 7}' ) es = get_elasticsearch_client(hosts=["http://localhost:9200"]) index = "test-index" @@ -439,12 +457,26 @@ def request_hook(span, method, url, kwargs): "GET", spans[0].attributes[request_hook_method_attribute] ) expected_url = f"/{index}/_doc/{doc_id}" + if major_version == 8: + expected_url += "?realtime=true&refresh=true" self.assertEqual( expected_url, spans[0].attributes[request_hook_url_attribute], ) - if major_version == 7: + if major_version == 8: + expected_kwargs = { + "body": None, + "request_timeout": "", + "max_retries": "", + "retry_on_status": "", + "retry_on_timeout": "", + "client_meta": "", + "headers": { + "accept": "application/vnd.elasticsearch+json; compatible-with=8" + }, + } + elif major_version == 7: expected_kwargs = { **kwargs, "headers": {"accept": "application/json"}, @@ -452,8 +484,8 @@ def request_hook(span, method, url, kwargs): else: expected_kwargs = {**kwargs} self.assertEqual( - json.dumps(expected_kwargs), - spans[0].attributes[request_hook_kwargs_attribute], + expected_kwargs, + json.loads(spans[0].attributes[request_hook_kwargs_attribute]), ) def test_response_hook(self, request_mock): @@ -492,7 +524,9 @@ def response_hook(span, response): }, } - request_mock.return_value = (1, {}, json.dumps(response_payload)) + request_mock.return_value = helpers.mock_response( + json.dumps(response_payload) + ) es = get_elasticsearch_client(hosts=["http://localhost:9200"]) es.get( index="test-index", **normalize_arguments(doc_type="_doc"), id=1 @@ -512,7 +546,7 @@ def test_no_op_tracer_provider(self, request_mock): tracer_provider=trace.NoOpTracerProvider() ) response_payload = '{"found": false, "timed_out": true, "took": 7}' - request_mock.return_value = (1, {}, response_payload) + request_mock.return_value = helpers.mock_response(response_payload) es = get_elasticsearch_client(hosts=["http://localhost:9200"]) res = es.get( index="test-index", **normalize_arguments(doc_type="_doc"), id=1 @@ -543,7 +577,7 @@ def test_body_sanitization(self, _): ) def test_bulk(self, request_mock): - request_mock.return_value = (1, {}, "{}") + request_mock.return_value = helpers.mock_response("{}") es = get_elasticsearch_client(hosts=["http://localhost:9200"]) es.bulk( diff --git a/tox.ini b/tox.ini index ae11e0f24f..fecc9e5af7 100644 --- a/tox.ini +++ b/tox.ini @@ -92,8 +92,9 @@ envlist = ; below mean these dependencies are being used: ; 0: elasticsearch-dsl==6.4.0 elasticsearch==6.8.2 ; 1: elasticsearch-dsl==7.4.1 elasticsearch==7.17.9 - py3{8,9,10,11}-test-instrumentation-elasticsearch-{0,1} - pypy3-test-instrumentation-elasticsearch-{0,1} + ; 2: elasticsearch-dsl>=8.0,<8.13 elasticsearch>=8.0,<8.13 + py3{8,9,10,11}-test-instrumentation-elasticsearch-{0,1,2} + pypy3-test-instrumentation-elasticsearch-{0,1,2} lint-instrumentation-elasticsearch ; opentelemetry-instrumentation-falcon @@ -716,7 +717,8 @@ commands_pre = elasticsearch: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils elasticsearch-0: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-0.txt elasticsearch-1: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt - lint-instrumentation-elasticsearch: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt + elasticsearch-2: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt + lint-instrumentation-elasticsearch: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt asyncio: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api asyncio: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions From d0500c2f8a2160ef21ae4ef3fa3f522f3eea7f94 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 14 May 2024 15:26:31 -0500 Subject: [PATCH 42/58] Add error handling to opentelemetry-bootstrap -a (#2517) * Revert "Refactor bootstrap generation (#2101)" This reverts commit 1ee7261ea7117fbd22e2262e488402213a874125. * Add error handling to opentelemetry-bootstrap -a Fixes #2516 --------- Co-authored-by: Tammy Baylis <96076570+tammy-baylis-swi@users.noreply.github.com> --- .../instrumentation/bootstrap.py | 41 ++++++++++------- .../instrumentation/bootstrap_gen.py | 5 ++ scripts/otel_packaging.py | 46 +++++++------------ 3 files changed, 47 insertions(+), 45 deletions(-) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py index 0c8f0aa3c4..6f86a539b2 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py @@ -14,8 +14,14 @@ import argparse import logging -import subprocess import sys +from subprocess import ( + PIPE, + CalledProcessError, + Popen, + SubprocessError, + check_call, +) import pkg_resources @@ -34,7 +40,7 @@ def wrapper(package=None): if package: return func(package) return func() - except subprocess.SubprocessError as exp: + except SubprocessError as exp: cmd = getattr(exp, "cmd", None) if cmd: msg = f'Error calling system command "{" ".join(cmd)}"' @@ -48,18 +54,21 @@ def wrapper(package=None): @_syscall def _sys_pip_install(package): # explicit upgrade strategy to override potential pip config - subprocess.check_call( - [ - sys.executable, - "-m", - "pip", - "install", - "-U", - "--upgrade-strategy", - "only-if-needed", - package, - ] - ) + try: + check_call( + [ + sys.executable, + "-m", + "pip", + "install", + "-U", + "--upgrade-strategy", + "only-if-needed", + package, + ] + ) + except CalledProcessError as error: + print(error) def _pip_check(): @@ -70,8 +79,8 @@ def _pip_check(): 'opentelemetry-instrumentation-flask 1.0.1 has requirement opentelemetry-sdk<2.0,>=1.0, but you have opentelemetry-sdk 0.5.' To not be too restrictive, we'll only check for relevant packages. """ - with subprocess.Popen( - [sys.executable, "-m", "pip", "check"], stdout=subprocess.PIPE + with Popen( + [sys.executable, "-m", "pip", "check"], stdout=PIPE ) as check_pipe: pip_check = check_pipe.communicate()[0].decode() pip_check_lower = pip_check.lower() diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py index 55d2f498a1..9eebd5bb38 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py @@ -24,6 +24,10 @@ "library": "aiohttp ~= 3.0", "instrumentation": "opentelemetry-instrumentation-aiohttp-client==0.46b0.dev", }, + { + "library": "aiohttp ~= 3.0", + "instrumentation": "opentelemetry-instrumentation-aiohttp-server==0.46b0.dev", + }, { "library": "aiopg >= 0.13.0, < 2.0.0", "instrumentation": "opentelemetry-instrumentation-aiopg==0.46b0.dev", @@ -187,6 +191,7 @@ "opentelemetry-instrumentation-dbapi==0.46b0.dev", "opentelemetry-instrumentation-logging==0.46b0.dev", "opentelemetry-instrumentation-sqlite3==0.46b0.dev", + "opentelemetry-instrumentation-threading==0.46b0.dev", "opentelemetry-instrumentation-urllib==0.46b0.dev", "opentelemetry-instrumentation-wsgi==0.46b0.dev", ] diff --git a/scripts/otel_packaging.py b/scripts/otel_packaging.py index c6c11c45fa..2f42e44189 100644 --- a/scripts/otel_packaging.py +++ b/scripts/otel_packaging.py @@ -12,55 +12,43 @@ # See the License for the specific language governing permissions and # limitations under the License. -from tomli import load -from os import path, listdir -from subprocess import check_output, CalledProcessError -from requests import get +import os +import subprocess +from subprocess import CalledProcessError -scripts_path = path.dirname(path.abspath(__file__)) -root_path = path.dirname(scripts_path) -instrumentations_path = path.join(root_path, "instrumentation") +import tomli + +scripts_path = os.path.dirname(os.path.abspath(__file__)) +root_path = os.path.dirname(scripts_path) +instrumentations_path = os.path.join(root_path, "instrumentation") def get_instrumentation_packages(): - for pkg in sorted(listdir(instrumentations_path)): - pkg_path = path.join(instrumentations_path, pkg) - if not path.isdir(pkg_path): + for pkg in sorted(os.listdir(instrumentations_path)): + pkg_path = os.path.join(instrumentations_path, pkg) + if not os.path.isdir(pkg_path): continue - error = f"Could not get version for package {pkg}" - try: - hatch_version = check_output( + version = subprocess.check_output( "hatch version", shell=True, cwd=pkg_path, - universal_newlines=True + universal_newlines=True, ) - except CalledProcessError as exc: print(f"Could not get hatch version from path {pkg_path}") print(exc.output) + raise exc - try: - response = get(f"https://pypi.org/pypi/{pkg}/json", timeout=10) - - except Exception: - print(error) - continue - - if response.status_code != 200: - print(error) - continue - - pyproject_toml_path = path.join(pkg_path, "pyproject.toml") + pyproject_toml_path = os.path.join(pkg_path, "pyproject.toml") with open(pyproject_toml_path, "rb") as file: - pyproject_toml = load(file) + pyproject_toml = tomli.load(file) instrumentation = { "name": pyproject_toml["project"]["name"], - "version": hatch_version.strip(), + "version": version.strip(), "instruments": pyproject_toml["project"]["optional-dependencies"][ "instruments" ], From 460fc335836c395db8472ecf464e7ecd94c08925 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Wed, 15 May 2024 03:52:59 -0600 Subject: [PATCH 43/58] Fix typo in sample code (#2494) Co-authored-by: Riccardo Magliocchetti --- .../src/opentelemetry/instrumentation/tornado/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py index 5c99457a39..be9129bda0 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py @@ -87,14 +87,14 @@ def client_request_hook(span, request): # will be called after a outgoing request made with # `tornado.httpclient.AsyncHTTPClient.fetch` finishes. # `response`` is an instance of ``Future[tornado.httpclient.HTTPResponse]`. - def client_resposne_hook(span, future): + def client_response_hook(span, future): pass # apply tornado instrumentation with hooks TornadoInstrumentor().instrument( server_request_hook=server_request_hook, client_request_hook=client_request_hook, - client_response_hook=client_resposne_hook + client_response_hook=client_response_hook ) Capture HTTP request and response headers From f8758c6902ed725864ff677f739829aa6bce2078 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 16 May 2024 14:05:21 -0700 Subject: [PATCH 44/58] Implement functions resource detector (#2523) * Update .pylintrc * fn * Update CHANGELOG.md * commments * Add deployment.environment to functions detector * Revert "Add deployment.environment to functions detector" This reverts commit 5411759711b8bc9976705deb416d5ffd8f65590f. * Remove deployment.environment from readme * Release 0.1.5 --------- Co-authored-by: jeremydvoss --- .../CHANGELOG.md | 4 +- .../README.rst | 12 +- .../pyproject.toml | 1 + .../resource/detector/azure/__init__.py | 2 + .../resource/detector/azure/_constants.py | 6 + .../resource/detector/azure/_utils.py | 27 ++++- .../resource/detector/azure/app_service.py | 32 ++--- .../resource/detector/azure/functions.py | 68 +++++++++++ .../resource/detector/azure/version.py | 2 +- .../tests/test_app_service.py | 39 +++++++ .../tests/test_functions.py | 110 ++++++++++++++++++ 11 files changed, 274 insertions(+), 29 deletions(-) create mode 100644 resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/functions.py create mode 100644 resource/opentelemetry-resource-detector-azure/tests/test_functions.py diff --git a/resource/opentelemetry-resource-detector-azure/CHANGELOG.md b/resource/opentelemetry-resource-detector-azure/CHANGELOG.md index 8954fc5359..f77fce18f1 100644 --- a/resource/opentelemetry-resource-detector-azure/CHANGELOG.md +++ b/resource/opentelemetry-resource-detector-azure/CHANGELOG.md @@ -5,10 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## Version 0.1.5 (2024-05-16) - Ignore vm detector if already in other rps ([#2456](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2456)) +- Implement functions resource detector + ([#2523](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2523)) ## Version 0.1.4 (2024-04-05) diff --git a/resource/opentelemetry-resource-detector-azure/README.rst b/resource/opentelemetry-resource-detector-azure/README.rst index 6a376534ad..baf2dddbbe 100644 --- a/resource/opentelemetry-resource-detector-azure/README.rst +++ b/resource/opentelemetry-resource-detector-azure/README.rst @@ -60,7 +60,17 @@ The Azure App Service Resource Detector sets the following Resource Attributes: * ``service.instance.id`` set to the value of the ``WEBSITE_INSTANCE_ID`` environment variable. * ``azure.app.service.stamp`` set to the value of the ``WEBSITE_HOME_STAMPNAME`` environment variable. -The Azure VM Resource Detector sets the following Resource Attributes according to the response from the `Azure Metadata Service `_: + The Azure Functions Resource Detector sets the following Resource Attributes: + * ``service.name`` set to the value of the ``WEBSITE_SITE_NAME`` environment variable. + * ``process.id`` set to the process ID collected from the running process. + * ``cloud.platform`` set to ``azure_functions``. + * ``cloud.provider`` set to ``azure``. + * ``cloud.resource_id`` set using the ``WEBSITE_RESOURCE_GROUP``, ``WEBSITE_OWNER_NAME``, and ``WEBSITE_SITE_NAME`` environment variables. + * ``cloud.region`` set to the value of the ``REGION_NAME`` environment variable. + * ``faas.instance`` set to the value of the ``WEBSITE_INSTANCE_ID`` environment variable. + * ``faas.max_memory`` set to the value of the ``WEBSITE_MEMORY_LIMIT_MB`` environment variable. + +The Azure VM Resource Detector sets the following Resource Attributes according to the response from the `Azure Metadata Service `_: * ``azure.vm.scaleset.name`` set to the value of the ``vmScaleSetName`` field. * ``azure.vm.sku`` set to the value of the ``sku`` field. * ``cloud.platform`` set to the value of the ``azure_vm``. diff --git a/resource/opentelemetry-resource-detector-azure/pyproject.toml b/resource/opentelemetry-resource-detector-azure/pyproject.toml index 72260709f9..efa1b24ee7 100644 --- a/resource/opentelemetry-resource-detector-azure/pyproject.toml +++ b/resource/opentelemetry-resource-detector-azure/pyproject.toml @@ -29,6 +29,7 @@ dependencies = [ [project.entry-points.opentelemetry_resource_detector] azure_app_service = "opentelemetry.resource.detector.azure.app_service:AzureAppServiceResourceDetector" +azure_functions = "opentelemetry.resource.detector.azure.functions:AzureFunctionsResourceDetector" azure_vm = "opentelemetry.resource.detector.azure.vm:AzureVMResourceDetector" [project.urls] diff --git a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/__init__.py b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/__init__.py index 913b677c3e..628a8ab781 100644 --- a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/__init__.py +++ b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/__init__.py @@ -15,11 +15,13 @@ # pylint: disable=import-error from .app_service import AzureAppServiceResourceDetector +from .functions import AzureFunctionsResourceDetector from .version import __version__ from .vm import AzureVMResourceDetector __all__ = [ "AzureAppServiceResourceDetector", + "AzureFunctionsResourceDetector", "AzureVMResourceDetector", "__version__", ] diff --git a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_constants.py b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_constants.py index dddc6632ac..3a6415e0d5 100644 --- a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_constants.py +++ b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_constants.py @@ -43,6 +43,12 @@ # Functions _FUNCTIONS_WORKER_RUNTIME = "FUNCTIONS_WORKER_RUNTIME" +_WEBSITE_MEMORY_LIMIT_MB = "WEBSITE_MEMORY_LIMIT_MB" + +_FUNCTIONS_ATTRIBUTE_ENV_VARS = { + ResourceAttributes.FAAS_INSTANCE: _WEBSITE_INSTANCE_ID, + ResourceAttributes.FAAS_MAX_MEMORY: _WEBSITE_MEMORY_LIMIT_MB, +} # Vm diff --git a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_utils.py b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_utils.py index 3f73613945..62d00c5a6c 100644 --- a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_utils.py +++ b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_utils.py @@ -11,27 +11,44 @@ # 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 +from os import environ +from typing import Optional from ._constants import ( _AKS_ARM_NAMESPACE_ID, _FUNCTIONS_WORKER_RUNTIME, + _WEBSITE_OWNER_NAME, + _WEBSITE_RESOURCE_GROUP, _WEBSITE_SITE_NAME, ) def _is_on_aks() -> bool: - return os.environ.get(_AKS_ARM_NAMESPACE_ID) is not None + return environ.get(_AKS_ARM_NAMESPACE_ID) is not None def _is_on_app_service() -> bool: - return os.environ.get(_WEBSITE_SITE_NAME) is not None + return environ.get(_WEBSITE_SITE_NAME) is not None def _is_on_functions() -> bool: - return os.environ.get(_FUNCTIONS_WORKER_RUNTIME) is not None + return environ.get(_FUNCTIONS_WORKER_RUNTIME) is not None def _can_ignore_vm_detect() -> bool: return _is_on_aks() or _is_on_app_service() or _is_on_functions() + + +def _get_azure_resource_uri() -> Optional[str]: + website_site_name = environ.get(_WEBSITE_SITE_NAME) + website_resource_group = environ.get(_WEBSITE_RESOURCE_GROUP) + website_owner_name = environ.get(_WEBSITE_OWNER_NAME) + + subscription_id = website_owner_name + if website_owner_name and "+" in website_owner_name: + subscription_id = website_owner_name[0 : website_owner_name.index("+")] + + if not (website_site_name and website_resource_group and subscription_id): + return None + + return f"/subscriptions/{subscription_id}/resourceGroups/{website_resource_group}/providers/Microsoft.Web/sites/{website_site_name}" diff --git a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/app_service.py b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/app_service.py index 613d8f9410..41371b8eec 100644 --- a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/app_service.py +++ b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/app_service.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Optional from os import environ from opentelemetry.sdk.resources import Resource, ResourceDetector @@ -20,29 +21,32 @@ CloudProviderValues, ResourceAttributes, ) +from opentelemetry.resource.detector.azure._utils import _get_azure_resource_uri from ._constants import ( _APP_SERVICE_ATTRIBUTE_ENV_VARS, - _WEBSITE_OWNER_NAME, - _WEBSITE_RESOURCE_GROUP, _WEBSITE_SITE_NAME, ) +from opentelemetry.resource.detector.azure._utils import _is_on_functions + class AzureAppServiceResourceDetector(ResourceDetector): def detect(self) -> Resource: attributes = {} website_site_name = environ.get(_WEBSITE_SITE_NAME) if website_site_name: - attributes[ResourceAttributes.SERVICE_NAME] = website_site_name + # Functions resource detector takes priority with `service.name` and `cloud.platform` + if not _is_on_functions(): + attributes[ResourceAttributes.SERVICE_NAME] = website_site_name + attributes[ResourceAttributes.CLOUD_PLATFORM] = ( + CloudPlatformValues.AZURE_APP_SERVICE.value + ) attributes[ResourceAttributes.CLOUD_PROVIDER] = ( CloudProviderValues.AZURE.value ) - attributes[ResourceAttributes.CLOUD_PLATFORM] = ( - CloudPlatformValues.AZURE_APP_SERVICE.value - ) - azure_resource_uri = _get_azure_resource_uri(website_site_name) + azure_resource_uri = _get_azure_resource_uri() if azure_resource_uri: attributes[ResourceAttributes.CLOUD_RESOURCE_ID] = ( azure_resource_uri @@ -53,17 +57,3 @@ def detect(self) -> Resource: attributes[key] = value return Resource(attributes) - - -def _get_azure_resource_uri(website_site_name): - website_resource_group = environ.get(_WEBSITE_RESOURCE_GROUP) - website_owner_name = environ.get(_WEBSITE_OWNER_NAME) - - subscription_id = website_owner_name - if website_owner_name and "+" in website_owner_name: - subscription_id = website_owner_name[0 : website_owner_name.index("+")] - - if not (website_resource_group and subscription_id): - return None - - return f"/subscriptions/{subscription_id}/resourceGroups/{website_resource_group}/providers/Microsoft.Web/sites/{website_site_name}" diff --git a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/functions.py b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/functions.py new file mode 100644 index 0000000000..0bf9a10f86 --- /dev/null +++ b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/functions.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. + +from os import environ, getpid + +from opentelemetry.sdk.resources import Resource, ResourceDetector +from opentelemetry.semconv.resource import ( + CloudPlatformValues, + CloudProviderValues, + ResourceAttributes, +) + +from ._constants import ( + _FUNCTIONS_ATTRIBUTE_ENV_VARS, + _REGION_NAME, + _WEBSITE_SITE_NAME, +) +from opentelemetry.resource.detector.azure._utils import ( + _get_azure_resource_uri, + _is_on_functions, +) + + +class AzureFunctionsResourceDetector(ResourceDetector): + def detect(self) -> Resource: + attributes = {} + if _is_on_functions(): + website_site_name = environ.get(_WEBSITE_SITE_NAME) + if website_site_name: + attributes[ResourceAttributes.SERVICE_NAME] = website_site_name + attributes[ResourceAttributes.PROCESS_PID] = getpid() + attributes[ResourceAttributes.CLOUD_PROVIDER] = ( + CloudProviderValues.AZURE.value + ) + attributes[ResourceAttributes.CLOUD_PLATFORM] = ( + CloudPlatformValues.AZURE_FUNCTIONS.value + ) + cloud_region = environ.get(_REGION_NAME) + if cloud_region: + attributes[ResourceAttributes.CLOUD_REGION] = cloud_region + azure_resource_uri = _get_azure_resource_uri() + if azure_resource_uri: + attributes[ResourceAttributes.CLOUD_RESOURCE_ID] = ( + azure_resource_uri + ) + for key, env_var in _FUNCTIONS_ATTRIBUTE_ENV_VARS.items(): + value = environ.get(env_var) + if value: + if key == ResourceAttributes.FAAS_MAX_MEMORY: + try: + value = int(value) + except ValueError: + continue + attributes[key] = value + + return Resource(attributes) + diff --git a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/version.py b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/version.py index f961659f70..fac29d773f 100644 --- a/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/version.py +++ b/resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.1.4" +__version__ = "0.1.5" diff --git a/resource/opentelemetry-resource-detector-azure/tests/test_app_service.py b/resource/opentelemetry-resource-detector-azure/tests/test_app_service.py index c5d2396dab..6c3d395994 100644 --- a/resource/opentelemetry-resource-detector-azure/tests/test_app_service.py +++ b/resource/opentelemetry-resource-detector-azure/tests/test_app_service.py @@ -68,6 +68,45 @@ def test_on_app_service(self): self.assertEqual( attributes["azure.app.service.stamp"], TEST_WEBSITE_HOME_STAMPNAME ) + + @patch.dict( + "os.environ", + { + "FUNCTIONS_WORKER_RUNTIME": "1", + "WEBSITE_SITE_NAME": TEST_WEBSITE_SITE_NAME, + "REGION_NAME": TEST_REGION_NAME, + "WEBSITE_SLOT_NAME": TEST_WEBSITE_SLOT_NAME, + "WEBSITE_HOSTNAME": TEST_WEBSITE_HOSTNAME, + "WEBSITE_INSTANCE_ID": TEST_WEBSITE_INSTANCE_ID, + "WEBSITE_HOME_STAMPNAME": TEST_WEBSITE_HOME_STAMPNAME, + "WEBSITE_RESOURCE_GROUP": TEST_WEBSITE_RESOURCE_GROUP, + "WEBSITE_OWNER_NAME": TEST_WEBSITE_OWNER_NAME, + }, + clear=True, + ) + def test_on_app_service_with_functions(self): + resource = AzureAppServiceResourceDetector().detect() + attributes = resource.attributes + self.assertIsNone(attributes.get("service.name")) + self.assertEqual(attributes["cloud.provider"], "azure") + self.assertIsNone(attributes.get("cloud.platform")) + + self.assertEqual( + attributes["cloud.resource_id"], + f"/subscriptions/{TEST_WEBSITE_OWNER_NAME}/resourceGroups/{TEST_WEBSITE_RESOURCE_GROUP}/providers/Microsoft.Web/sites/{TEST_WEBSITE_SITE_NAME}", + ) + + self.assertEqual(attributes["cloud.region"], TEST_REGION_NAME) + self.assertEqual( + attributes["deployment.environment"], TEST_WEBSITE_SLOT_NAME + ) + self.assertEqual(attributes["host.id"], TEST_WEBSITE_HOSTNAME) + self.assertEqual( + attributes["service.instance.id"], TEST_WEBSITE_INSTANCE_ID + ) + self.assertEqual( + attributes["azure.app.service.stamp"], TEST_WEBSITE_HOME_STAMPNAME + ) @patch.dict( "os.environ", diff --git a/resource/opentelemetry-resource-detector-azure/tests/test_functions.py b/resource/opentelemetry-resource-detector-azure/tests/test_functions.py new file mode 100644 index 0000000000..1f5354c500 --- /dev/null +++ b/resource/opentelemetry-resource-detector-azure/tests/test_functions.py @@ -0,0 +1,110 @@ +# 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 unittest +from unittest.mock import patch + +# pylint: disable=no-name-in-module +from opentelemetry.resource.detector.azure.functions import ( + AzureFunctionsResourceDetector, +) + +TEST_WEBSITE_SITE_NAME = "TEST_WEBSITE_SITE_NAME" +TEST_REGION_NAME = "TEST_REGION_NAME" +TEST_WEBSITE_INSTANCE_ID = "TEST_WEBSITE_INSTANCE_ID" + +TEST_WEBSITE_RESOURCE_GROUP = "TEST_WEBSITE_RESOURCE_GROUP" +TEST_WEBSITE_OWNER_NAME = "TEST_WEBSITE_OWNER_NAME" +TEST_WEBSITE_MEMORY_LIMIT_MB = "1024" + + +class TestAzureAppServiceResourceDetector(unittest.TestCase): + @patch.dict( + "os.environ", + { + "FUNCTIONS_WORKER_RUNTIME": "1", + "WEBSITE_SITE_NAME": TEST_WEBSITE_SITE_NAME, + "REGION_NAME": TEST_REGION_NAME, + "WEBSITE_INSTANCE_ID": TEST_WEBSITE_INSTANCE_ID, + "WEBSITE_RESOURCE_GROUP": TEST_WEBSITE_RESOURCE_GROUP, + "WEBSITE_OWNER_NAME": TEST_WEBSITE_OWNER_NAME, + "WEBSITE_MEMORY_LIMIT_MB": TEST_WEBSITE_MEMORY_LIMIT_MB, + }, + clear=True, + ) + @patch("opentelemetry.resource.detector.azure.functions.getpid") + def test_on_functions(self, pid_mock): + pid_mock.return_value = 1000 + resource = AzureFunctionsResourceDetector().detect() + attributes = resource.attributes + self.assertEqual(attributes["service.name"], TEST_WEBSITE_SITE_NAME) + self.assertEqual(attributes["cloud.provider"], "azure") + self.assertEqual(attributes["cloud.platform"], "azure_functions") + self.assertEqual(attributes["process.pid"], 1000) + + self.assertEqual( + attributes["cloud.resource_id"], + f"/subscriptions/{TEST_WEBSITE_OWNER_NAME}/resourceGroups/{TEST_WEBSITE_RESOURCE_GROUP}/providers/Microsoft.Web/sites/{TEST_WEBSITE_SITE_NAME}", + ) + + self.assertEqual(attributes["cloud.region"], TEST_REGION_NAME) + self.assertEqual(attributes["faas.instance"], TEST_WEBSITE_INSTANCE_ID) + self.assertEqual(attributes["faas.max_memory"], 1024) + + @patch.dict( + "os.environ", + { + "FUNCTIONS_WORKER_RUNTIME": "1", + "WEBSITE_SITE_NAME": TEST_WEBSITE_SITE_NAME, + "REGION_NAME": TEST_REGION_NAME, + "WEBSITE_INSTANCE_ID": TEST_WEBSITE_INSTANCE_ID, + "WEBSITE_RESOURCE_GROUP": TEST_WEBSITE_RESOURCE_GROUP, + "WEBSITE_OWNER_NAME": TEST_WEBSITE_OWNER_NAME, + "WEBSITE_MEMORY_LIMIT_MB": "error", + }, + clear=True, + ) + @patch("opentelemetry.resource.detector.azure.functions.getpid") + def test_on_functions_error_memory(self, pid_mock): + pid_mock.return_value = 1000 + resource = AzureFunctionsResourceDetector().detect() + attributes = resource.attributes + self.assertEqual(attributes["service.name"], TEST_WEBSITE_SITE_NAME) + self.assertEqual(attributes["cloud.provider"], "azure") + self.assertEqual(attributes["cloud.platform"], "azure_functions") + self.assertEqual(attributes["process.pid"], 1000) + + self.assertEqual( + attributes["cloud.resource_id"], + f"/subscriptions/{TEST_WEBSITE_OWNER_NAME}/resourceGroups/{TEST_WEBSITE_RESOURCE_GROUP}/providers/Microsoft.Web/sites/{TEST_WEBSITE_SITE_NAME}", + ) + + self.assertEqual(attributes["cloud.region"], TEST_REGION_NAME) + self.assertEqual(attributes["faas.instance"], TEST_WEBSITE_INSTANCE_ID) + self.assertIsNone(attributes.get("faas.max_memory")) + + @patch.dict( + "os.environ", + { + "WEBSITE_SITE_NAME": TEST_WEBSITE_SITE_NAME, + "REGION_NAME": TEST_REGION_NAME, + "WEBSITE_INSTANCE_ID": TEST_WEBSITE_INSTANCE_ID, + "WEBSITE_RESOURCE_GROUP": TEST_WEBSITE_RESOURCE_GROUP, + "WEBSITE_OWNER_NAME": TEST_WEBSITE_OWNER_NAME, + "WEBSITE_MEMORY_LIMIT_MB": TEST_WEBSITE_MEMORY_LIMIT_MB, + }, + clear=True, + ) + def test_off_app_service(self): + resource = AzureFunctionsResourceDetector().detect() + self.assertEqual(resource.attributes, {}) From f4f3042f85fa68565c4da3f96d437b15d78e1063 Mon Sep 17 00:00:00 2001 From: Povilas Versockas Date: Wed, 22 May 2024 07:48:54 +0300 Subject: [PATCH 45/58] fix(async-io): check for __name__ atribute when tracing coroutine (#2521) --- CHANGELOG.md | 2 + .../instrumentation/asyncio/__init__.py | 2 + .../tests/test_asyncio_anext.py | 56 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_anext.py diff --git a/CHANGELOG.md b/CHANGELOG.md index d10983c10b..b963cfddc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2474](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2474)) - `opentelemetry-instrumentation-elasticsearch` Improved support for version 8 ([#2420](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2420)) +- `opentelemetry-instrumentation-asyncio` Check for __name__ attribute in the coroutine + ([#2521](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2521)) ## Version 1.24.0/0.45b0 (2024-03-28) diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py b/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py index 72aa5fd2aa..6d82da6cd0 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py @@ -261,6 +261,8 @@ def trace_item(self, coro_or_future): return coro_or_future async def trace_coroutine(self, coro): + if not hasattr(coro, "__name__"): + return start = default_timer() attr = { "type": "coroutine", diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_anext.py b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_anext.py new file mode 100644 index 0000000000..9ce3fc4b33 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_anext.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 asyncio +from unittest.mock import patch + +# pylint: disable=no-name-in-module +from opentelemetry.instrumentation.asyncio import AsyncioInstrumentor +from opentelemetry.instrumentation.asyncio.environment_variables import ( + OTEL_PYTHON_ASYNCIO_COROUTINE_NAMES_TO_TRACE, +) +from opentelemetry.test.test_base import TestBase +from opentelemetry.trace import get_tracer + + +class TestAsyncioAnext(TestBase): + @patch.dict( + "os.environ", + {OTEL_PYTHON_ASYNCIO_COROUTINE_NAMES_TO_TRACE: "async_func"}, + ) + def setUp(self): + super().setUp() + AsyncioInstrumentor().instrument() + self._tracer = get_tracer( + __name__, + ) + + def tearDown(self): + super().tearDown() + AsyncioInstrumentor().uninstrument() + + # Asyncio anext() does not have __name__ attribute, which is used to determine if the coroutine should be traced. + # This test is to ensure that the instrumentation does not break when the coroutine does not have __name__ attribute. + def test_asyncio_anext(self): + async def main(): + async def async_gen(): + for it in range(2): + yield it + + async_gen_instance = async_gen() + agen = anext(async_gen_instance) + await asyncio.create_task(agen) + + asyncio.run(main()) + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 0) From c28f9b837f386cffd9b0f8800f7653a85ca5c60e Mon Sep 17 00:00:00 2001 From: Jeremy Voss Date: Wed, 22 May 2024 10:45:10 -0700 Subject: [PATCH 46/58] Update functions detector readme (#2533) --- .../README.rst | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/resource/opentelemetry-resource-detector-azure/README.rst b/resource/opentelemetry-resource-detector-azure/README.rst index baf2dddbbe..19a0f97f32 100644 --- a/resource/opentelemetry-resource-detector-azure/README.rst +++ b/resource/opentelemetry-resource-detector-azure/README.rst @@ -9,6 +9,7 @@ OpenTelemetry Resource detectors for Azure This library contains OpenTelemetry `Resource Detectors `_ for the following Azure resources: * `Azure App Service `_ * `Azure Virtual Machines `_ + * `Azure Functions (Experimental) `_ Installation ------------ @@ -60,16 +61,6 @@ The Azure App Service Resource Detector sets the following Resource Attributes: * ``service.instance.id`` set to the value of the ``WEBSITE_INSTANCE_ID`` environment variable. * ``azure.app.service.stamp`` set to the value of the ``WEBSITE_HOME_STAMPNAME`` environment variable. - The Azure Functions Resource Detector sets the following Resource Attributes: - * ``service.name`` set to the value of the ``WEBSITE_SITE_NAME`` environment variable. - * ``process.id`` set to the process ID collected from the running process. - * ``cloud.platform`` set to ``azure_functions``. - * ``cloud.provider`` set to ``azure``. - * ``cloud.resource_id`` set using the ``WEBSITE_RESOURCE_GROUP``, ``WEBSITE_OWNER_NAME``, and ``WEBSITE_SITE_NAME`` environment variables. - * ``cloud.region`` set to the value of the ``REGION_NAME`` environment variable. - * ``faas.instance`` set to the value of the ``WEBSITE_INSTANCE_ID`` environment variable. - * ``faas.max_memory`` set to the value of the ``WEBSITE_MEMORY_LIMIT_MB`` environment variable. - The Azure VM Resource Detector sets the following Resource Attributes according to the response from the `Azure Metadata Service `_: * ``azure.vm.scaleset.name`` set to the value of the ``vmScaleSetName`` field. * ``azure.vm.sku`` set to the value of the ``sku`` field. @@ -84,6 +75,16 @@ The Azure VM Resource Detector sets the following Resource Attributes according * ``os.version`` set to the value of the ``version`` field. * ``service.instance.id`` set to the value of the ``vmId`` field. +The Azure Functions Resource Detector is currently experimental. It sets the following Resource Attributes: + * ``service.name`` set to the value of the ``WEBSITE_SITE_NAME`` environment variable. + * ``process.id`` set to the process ID collected from the running process. + * ``cloud.platform`` set to ``azure_functions``. + * ``cloud.provider`` set to ``azure``. + * ``cloud.resource_id`` set using the ``WEBSITE_RESOURCE_GROUP``, ``WEBSITE_OWNER_NAME``, and ``WEBSITE_SITE_NAME`` environment variables. + * ``cloud.region`` set to the value of the ``REGION_NAME`` environment variable. + * ``faas.instance`` set to the value of the ``WEBSITE_INSTANCE_ID`` environment variable. + * ``faas.max_memory`` set to the value of the ``WEBSITE_MEMORY_LIMIT_MB`` environment variable. + For more information, see the `Semantic Conventions for Cloud Resource Attributes `_. References From da75015fade9f8adf58416fdeb8684a4a263d7b8 Mon Sep 17 00:00:00 2001 From: Adin Hodovic Date: Thu, 23 May 2024 11:02:46 +0200 Subject: [PATCH 47/58] fix: Ensure compability with Psycopg3 to extract libpq build version (#2500) * fix: Ensure compability with Psycopg3 to extract libpq build version Struggling with getting dbapi and psycopg3 working. Think this is the error, __libpq_version does not exist on psycopg3 https://github.com/psycopg/psycopg/blob/master/psycopg/psycopg/pq/pq_ctypes.py#L1220 * docs: Add changelog entry * docs: Fix spelling --------- Co-authored-by: Riccardo Magliocchetti Co-authored-by: Shalev Roda <65566801+shalevr@users.noreply.github.com> --- CHANGELOG.md | 4 +-- .../instrumentation/dbapi/__init__.py | 9 ++++++- .../tests/test_dbapi_integration.py | 26 +++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b963cfddc2..038706f35c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- `opentelemetry-instrumentation-dbapi` Fix compatibility with Psycopg3 to extract libpq build version (#2500)[https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2500] - `opentelemetry-instrumentation-grpc` AioClientInterceptor should propagate with a Metadata object ([#2363](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2363)) - `opentelemetry-instrumentation-boto3sqs` Instrument Session and resource @@ -126,7 +127,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#1959](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1959)) - `opentelemetry-resource-detector-azure` Added dependency for Cloud Resource ID attribute ([#2072](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2072)) - + ## Version 1.21.0/0.42b0 (2023-11-01) ### Added @@ -1540,4 +1541,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-resource-detector-azure` Suppress instrumentation for `urllib` call ([#2178](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2178)) - AwsLambdaInstrumentor handles and re-raises function exception ([#2245](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2245)) - diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py b/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py index b0acbed185..0857d2989b 100644 --- a/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py @@ -427,12 +427,19 @@ def traced_execution( if args and self._commenter_enabled: try: args_list = list(args) + if hasattr(self._connect_module, "__libpq_version__"): + libpq_version = self._connect_module.__libpq_version__ + else: + libpq_version = ( + self._connect_module.pq.__build_version__ + ) + commenter_data = { # Psycopg2/framework information "db_driver": f"psycopg2:{self._connect_module.__version__.split(' ')[0]}", "dbapi_threadsafety": self._connect_module.threadsafety, "dbapi_level": self._connect_module.apilevel, - "libpq_version": self._connect_module.__libpq_version__, + "libpq_version": libpq_version, "driver_paramstyle": self._connect_module.paramstyle, } if self._commenter_options.get( diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py b/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py index 0d19ce8373..d0835e93e6 100644 --- a/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py +++ b/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py @@ -275,6 +275,32 @@ def test_executemany_comment(self): r"Select 1 /\*dbapi_threadsafety=123,driver_paramstyle='test',libpq_version=123,traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;", ) + def test_compatible_build_version_psycopg_psycopg2_libpq(self): + connect_module = mock.MagicMock() + connect_module.__version__ = mock.MagicMock() + connect_module.pq = mock.MagicMock() + connect_module.pq.__build_version__ = 123 + connect_module.apilevel = 123 + connect_module.threadsafety = 123 + connect_module.paramstyle = "test" + + db_integration = dbapi.DatabaseApiIntegration( + "testname", + "testcomponent", + enable_commenter=True, + commenter_options={"db_driver": False, "dbapi_level": False}, + connect_module=connect_module, + ) + mock_connection = db_integration.wrapped_connection( + mock_connect, {}, {} + ) + cursor = mock_connection.cursor() + cursor.executemany("Select 1;") + self.assertRegex( + cursor.query, + r"Select 1 /\*dbapi_threadsafety=123,driver_paramstyle='test',libpq_version=123,traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;", + ) + def test_executemany_flask_integration_comment(self): connect_module = mock.MagicMock() connect_module.__version__ = mock.MagicMock() From 66a107fa49fc6d264ae6d690c09c9f04bc4a5dd7 Mon Sep 17 00:00:00 2001 From: Povilas Versockas Date: Fri, 24 May 2024 02:28:50 +0300 Subject: [PATCH 48/58] fix(async-io): return coro when __name__ is not present (#2541) --- .../src/opentelemetry/instrumentation/asyncio/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py b/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py index 6d82da6cd0..fc1b535270 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py @@ -262,7 +262,7 @@ def trace_item(self, coro_or_future): async def trace_coroutine(self, coro): if not hasattr(coro, "__name__"): - return + return coro start = default_timer() attr = { "type": "coroutine", From 78285a5795db18e25126f1452a92a536a611b1be Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Fri, 24 May 2024 10:49:18 -0700 Subject: [PATCH 49/58] Pin codespell version to fix builds (#2550) --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index fecc9e5af7..0f1e8badcc 100644 --- a/tox.ini +++ b/tox.ini @@ -1206,7 +1206,7 @@ commands = basepython: python3 recreate = True deps = - codespell + codespell==2.2.6 commands = codespell From c1a51fde96c0f53ca185c3816be9fe69cba4f528 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Fri, 24 May 2024 19:57:29 +0200 Subject: [PATCH 50/58] Pre Python 3.12 enablement fixes (#2529) --- .../test-requirements.txt | 2 +- .../tests/test_botocore_dynamodb.py | 12 ++++++------ .../tests/test_utils.py | 2 +- tox.ini | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements.txt index 9dcf9f9c0d..7c11b1e063 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements.txt @@ -28,7 +28,7 @@ jsonschema==4.21.1 jsonschema-specifications==2023.12.1 junit-xml==1.9 MarkupSafe==2.0.1 -moto==2.2.20 +moto==3.1.19 mpmath==1.3.0 networkx==3.1 packaging==23.2 diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_dynamodb.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_dynamodb.py index 1b7f5bb0cb..12ebe8f2b7 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_dynamodb.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_dynamodb.py @@ -324,14 +324,14 @@ def test_get_item(self): Key={"id": {"S": "1"}}, ConsistentRead=True, AttributesToGet=["id"], - ProjectionExpression="1,2", + ProjectionExpression="PE", ReturnConsumedCapacity="TOTAL", ) span = self.assert_span("GetItem") self.assert_table_names(span, self.default_table_name) self.assert_consistent_read(span, True) - self.assert_projection(span, "1,2") + self.assert_projection(span, "PE") self.assert_consumed_capacity(span, self.default_table_name) @mock_dynamodb2 @@ -390,7 +390,7 @@ def test_query(self): } }, ScanIndexForward=True, - ProjectionExpression="1,2", + ProjectionExpression="PE", ReturnConsumedCapacity="TOTAL", ) @@ -403,7 +403,7 @@ def test_query(self): self.assert_consistent_read(span, True) self.assert_index_name(span, "lsi") self.assert_limit(span, 42) - self.assert_projection(span, "1,2") + self.assert_projection(span, "PE") self.assert_select(span, "ALL_ATTRIBUTES") self.assert_consumed_capacity(span, self.default_table_name) @@ -419,7 +419,7 @@ def test_scan(self): Select="ALL_ATTRIBUTES", TotalSegments=17, Segment=21, - ProjectionExpression="1,2", + ProjectionExpression="PE", ConsistentRead=True, ReturnConsumedCapacity="TOTAL", ) @@ -440,7 +440,7 @@ def test_scan(self): self.assert_consistent_read(span, True) self.assert_index_name(span, "lsi") self.assert_limit(span, 42) - self.assert_projection(span, "1,2") + self.assert_projection(span, "PE") self.assert_select(span, "ALL_ATTRIBUTES") self.assert_consumed_capacity(span, self.default_table_name) diff --git a/instrumentation/opentelemetry-instrumentation-pika/tests/test_utils.py b/instrumentation/opentelemetry-instrumentation-pika/tests/test_utils.py index d651ea64c9..7c0aa7a715 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/tests/test_utils.py +++ b/instrumentation/opentelemetry-instrumentation-pika/tests/test_utils.py @@ -558,7 +558,7 @@ def test_decorate_deque_proxy( self.assertEqual(res, evt) generator_info.pending_events.popleft.assert_called_once() extract.assert_not_called() - context_get_current.not_called() + context_get_current.assert_not_called() context_detach.assert_called_once() context_attach.assert_not_called() get_span.assert_not_called() diff --git a/tox.ini b/tox.ini index 0f1e8badcc..d6fb9eee10 100644 --- a/tox.ini +++ b/tox.ini @@ -1198,8 +1198,8 @@ deps = -r dev-requirements.txt commands = - black --config {toxinidir}/pyproject.toml {{toxinidir}} --diff --check - isort --settings-path {toxinidir}/.isort.cfg {{toxinidir}} --diff --check-only + black --config {toxinidir}/pyproject.toml {toxinidir} --diff --check + isort --settings-path {toxinidir}/.isort.cfg {toxinidir} --diff --check-only flake8 --config {toxinidir}/.flake8 {toxinidir} [testenv:spellcheck] From e6409568c11f5ec1341e85770f2f01dded676d7a Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Fri, 24 May 2024 20:12:53 +0200 Subject: [PATCH 51/58] Reenable pylint broad exception (#2536) --- .pylintrc | 2 +- .../tests/test_aiopg_integration.py | 11 +++++++---- .../tests/mocks/lambda_function.py | 1 + .../tests/test_dbapi_integration.py | 3 +++ .../tests/test_aio_client_interceptor_hooks.py | 4 ++-- .../tests/test_client_interceptor_hooks.py | 4 ++-- .../tests/test_psycopg_integration.py | 6 +++--- .../tests/test_urllib_integration.py | 2 +- 8 files changed, 20 insertions(+), 13 deletions(-) diff --git a/.pylintrc b/.pylintrc index 114dadef75..9f6c80faa9 100644 --- a/.pylintrc +++ b/.pylintrc @@ -492,4 +492,4 @@ min-public-methods=2 # Exceptions that will emit a warning when being caught. Defaults to # "Exception". -overgeneral-exceptions=Exception +overgeneral-exceptions=builtins.Exception diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py b/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py index fb76bd0f38..c497ae4564 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py @@ -17,6 +17,7 @@ from unittest.mock import MagicMock import aiopg +import psycopg2 import opentelemetry.instrumentation.aiopg from opentelemetry import trace as trace_api @@ -384,7 +385,9 @@ def test_span_failed(self): span.attributes[SpanAttributes.DB_STATEMENT], "Test query" ) self.assertIs(span.status.status_code, trace_api.StatusCode.ERROR) - self.assertEqual(span.status.description, "Exception: Test Exception") + self.assertEqual( + span.status.description, "ProgrammingError: Test Exception" + ) def test_executemany(self): db_integration = AiopgIntegration(self.tracer, "testcomponent") @@ -570,17 +573,17 @@ class MockCursor: # pylint: disable=unused-argument, no-self-use async def execute(self, query, params=None, throw_exception=False): if throw_exception: - raise Exception("Test Exception") + raise psycopg2.ProgrammingError("Test Exception") # pylint: disable=unused-argument, no-self-use async def executemany(self, query, params=None, throw_exception=False): if throw_exception: - raise Exception("Test Exception") + raise psycopg2.ProgrammingError("Test Exception") # pylint: disable=unused-argument, no-self-use async def callproc(self, query, params=None, throw_exception=False): if throw_exception: - raise Exception("Test Exception") + raise psycopg2.ProgrammingError("Test Exception") def close(self): pass diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/lambda_function.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/lambda_function.py index a878d0f06a..539c896a0b 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/lambda_function.py +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/lambda_function.py @@ -22,4 +22,5 @@ def rest_api_handler(event, context): def handler_exc(event, context): + # pylint: disable=broad-exception-raised raise Exception("500 internal server error") diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py b/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py index d0835e93e6..eb2d628a3a 100644 --- a/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py +++ b/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py @@ -419,11 +419,13 @@ def __init__(self) -> None: # pylint: disable=unused-argument, no-self-use def execute(self, query, params=None, throw_exception=False): if throw_exception: + # pylint: disable=broad-exception-raised raise Exception("Test Exception") # pylint: disable=unused-argument, no-self-use def executemany(self, query, params=None, throw_exception=False): if throw_exception: + # pylint: disable=broad-exception-raised raise Exception("Test Exception") self.query = query self.params = params @@ -431,4 +433,5 @@ def executemany(self, query, params=None, throw_exception=False): # pylint: disable=unused-argument, no-self-use def callproc(self, query, params=None, throw_exception=False): if throw_exception: + # pylint: disable=broad-exception-raised raise Exception("Test Exception") diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor_hooks.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor_hooks.py index fe906b26c1..9086d8b0f7 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor_hooks.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor_hooks.py @@ -47,11 +47,11 @@ def response_hook(span, response): def request_hook_with_exception(_span, _request): - raise Exception() + raise Exception() # pylint: disable=broad-exception-raised def response_hook_with_exception(_span, _response): - raise Exception() + raise Exception() # pylint: disable=broad-exception-raised @pytest.mark.asyncio diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_hooks.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_hooks.py index aeecffc71c..ac65c76c34 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_hooks.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_hooks.py @@ -73,11 +73,11 @@ def response_hook(span, response): def request_hook_with_exception(_span, _request): - raise Exception() + raise Exception() # pylint: disable=broad-exception-raised def response_hook_with_exception(_span, _response): - raise Exception() + raise Exception() # pylint: disable=broad-exception-raised class TestHooks(TestBase): diff --git a/instrumentation/opentelemetry-instrumentation-psycopg/tests/test_psycopg_integration.py b/instrumentation/opentelemetry-instrumentation-psycopg/tests/test_psycopg_integration.py index 4fbcac6042..e2b3ed917a 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg/tests/test_psycopg_integration.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg/tests/test_psycopg_integration.py @@ -53,17 +53,17 @@ def __init__(self, *args, **kwargs): # pylint: disable=unused-argument, no-self-use async def execute(self, query, params=None, throw_exception=False): if throw_exception: - raise Exception("Test Exception") + raise psycopg.Error("Test Exception") # pylint: disable=unused-argument, no-self-use async def executemany(self, query, params=None, throw_exception=False): if throw_exception: - raise Exception("Test Exception") + raise psycopg.Error("Test Exception") # pylint: disable=unused-argument, no-self-use async def callproc(self, query, params=None, throw_exception=False): if throw_exception: - raise Exception("Test Exception") + raise psycopg.Error("Test Exception") async def __aenter__(self, *args, **kwargs): return self diff --git a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py index 36189e12c1..f73f0d1b97 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py +++ b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py @@ -99,7 +99,7 @@ def timeout_exception_callback(*_, **__): @staticmethod def base_exception_callback(*_, **__): - raise Exception("test") + raise Exception("test") # pylint: disable=broad-exception-raised def assert_span(self, exporter=None, num_spans=1): if exporter is None: From 65b4f850a03135bff18a95e62465da881c25f0ec Mon Sep 17 00:00:00 2001 From: Tim Hutchinson Date: Fri, 24 May 2024 15:06:53 -0400 Subject: [PATCH 52/58] Preserve brackets around literal IPv6 hosts (#2552) --- CHANGELOG.md | 1 + .../src/opentelemetry/util/http/__init__.py | 6 +---- .../tests/test_remove_credentials.py | 27 +++++++++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 util/opentelemetry-util-http/tests/test_remove_credentials.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 038706f35c..3232e6fef8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2420](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2420)) - `opentelemetry-instrumentation-asyncio` Check for __name__ attribute in the coroutine ([#2521](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2521)) +- `opentelemetry-util-http` Preserve brackets around literal IPv6 hosts ([#2552](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2552)) ## Version 1.24.0/0.45b0 (2024-03-28) diff --git a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py index 1f7ce98937..e8a2cf2034 100644 --- a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py +++ b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py @@ -166,11 +166,7 @@ def remove_url_credentials(url: str) -> str: parsed = urlparse(url) if all([parsed.scheme, parsed.netloc]): # checks for valid url parsed_url = urlparse(url) - netloc = ( - (":".join(((parsed_url.hostname or ""), str(parsed_url.port)))) - if parsed_url.port - else (parsed_url.hostname or "") - ) + _, _, netloc = parsed.netloc.rpartition("@") return urlunparse( ( parsed_url.scheme, diff --git a/util/opentelemetry-util-http/tests/test_remove_credentials.py b/util/opentelemetry-util-http/tests/test_remove_credentials.py new file mode 100644 index 0000000000..b6243145f5 --- /dev/null +++ b/util/opentelemetry-util-http/tests/test_remove_credentials.py @@ -0,0 +1,27 @@ +import unittest + +from opentelemetry.util.http import remove_url_credentials + + +class TestRemoveUrlCredentials(unittest.TestCase): + def test_remove_no_credentials(self): + url = "http://opentelemetry.io:8080/test/path?query=value" + cleaned_url = remove_url_credentials(url) + self.assertEqual(cleaned_url, url) + + def test_remove_credentials(self): + url = "http://someuser:somepass@opentelemetry.io:8080/test/path?query=value" + cleaned_url = remove_url_credentials(url) + self.assertEqual( + cleaned_url, "http://opentelemetry.io:8080/test/path?query=value" + ) + + def test_remove_credentials_ipv4_literal(self): + url = "http://someuser:somepass@127.0.0.1:8080/test/path?query=value" + cleaned_url = remove_url_credentials(url) + self.assertEqual(cleaned_url, "http://127.0.0.1:8080/test/path?query=value") + + def test_remove_credentials_ipv6_literal(self): + url = "http://someuser:somepass@[::1]:8080/test/path?query=value" + cleaned_url = remove_url_credentials(url) + self.assertEqual(cleaned_url, "http://[::1]:8080/test/path?query=value") From eb8e45695e88510a611d2cc04f14cdf56036968a Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Fri, 24 May 2024 22:13:37 +0200 Subject: [PATCH 53/58] elasticsearch: don't produce spans if native elasticsearch support is enabled (#2524) --- CHANGELOG.md | 3 ++ .../instrumentation/elasticsearch/__init__.py | 27 +++++++++++++- .../test-requirements-2.txt | 6 ++-- .../tests/test_elasticsearch.py | 35 +++++++++++++++++-- tox.ini | 2 +- 5 files changed, 66 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3232e6fef8..a75e1537cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,10 +54,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2474](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2474)) - `opentelemetry-instrumentation-elasticsearch` Improved support for version 8 ([#2420](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2420)) +- `opentelemetry-instrumentation-elasticsearch` Disabling instrumentation with native OTel support enabled + ([#2524](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2524)) - `opentelemetry-instrumentation-asyncio` Check for __name__ attribute in the coroutine ([#2521](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2521)) - `opentelemetry-util-http` Preserve brackets around literal IPv6 hosts ([#2552](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2552)) + ## Version 1.24.0/0.45b0 (2024-03-28) ### Added diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py index acf4596fb0..f8d7920e20 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py @@ -16,6 +16,15 @@ This library allows tracing HTTP elasticsearch made by the `elasticsearch `_ library. +.. warning:: + The elasticsearch package got native OpenTelemetry support since version + `8.13 `_. + To avoid duplicated tracing this instrumentation disables itself if it finds an elasticsearch client + that has OpenTelemetry support enabled. + + Please be aware that the two libraries may use a different semantic convention, see + `elasticsearch documentation `_. + Usage ----- @@ -54,7 +63,7 @@ def response_hook(span: Span, response: dict) for example: -.. code: python +.. code-block: python from opentelemetry.instrumentation.elasticsearch import ElasticsearchInstrumentor import elasticsearch @@ -81,6 +90,7 @@ def response_hook(span, response): """ import re +import warnings from logging import getLogger from os import environ from typing import Collection @@ -197,6 +207,16 @@ def _wrap_perform_request( ): # pylint: disable=R0912,R0914 def wrapper(wrapped, _, args, kwargs): + # if wrapped elasticsearch has native OTel instrumentation just call the wrapped function + otel_span = kwargs.get("otel_span") + if otel_span and otel_span.otel_span: + warnings.warn( + "Instrumentation disabled, relying on elasticsearch native OTel support, see " + "https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/elasticsearch/elasticsearch.html", + Warning, + ) + return wrapped(*args, **kwargs) + method = url = None try: method, url, *_ = args @@ -249,6 +269,11 @@ def normalize_kwargs(k, v): v = str(v) elif isinstance(v, elastic_transport.HttpHeaders): v = dict(v) + elif isinstance( + v, elastic_transport.OpenTelemetrySpan + ): + # the transport Span is always a dummy one + v = None return (k, v) hook_kwargs = dict( diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt index 23d87f93dd..6b0d677ec7 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt @@ -1,9 +1,9 @@ asgiref==3.7.2 attrs==23.2.0 Deprecated==1.2.14 -elasticsearch==8.12.1 -elasticsearch-dsl==8.12.0 -elastic-transport==8.12.0 +elasticsearch==8.13.1 +elasticsearch-dsl==8.13.1 +elastic-transport==8.13.0 importlib-metadata==6.11.0 iniconfig==2.0.0 packaging==23.2 diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py index b0ee170329..b7e24d87c9 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py @@ -23,6 +23,7 @@ import elasticsearch.exceptions from elasticsearch import Elasticsearch from elasticsearch_dsl import Search +from pytest import mark import opentelemetry.instrumentation.elasticsearch from opentelemetry import trace @@ -36,7 +37,7 @@ from . import sanitization_queries # pylint: disable=no-name-in-module -major_version = elasticsearch.VERSION[0] +major_version, minor_version = elasticsearch.VERSION[:2] if major_version == 8: from . import helpers_es8 as helpers # pylint: disable=no-name-in-module @@ -70,6 +71,9 @@ def get_elasticsearch_client(*args, **kwargs): @mock.patch(helpers.perform_request_mock_path) +@mock.patch.dict( + os.environ, {"OTEL_PYTHON_INSTRUMENTATION_ELASTICSEARCH_ENABLED": "false"} +) class TestElasticsearchIntegration(TestBase): search_attributes = { SpanAttributes.DB_SYSTEM: "elasticsearch", @@ -110,7 +114,6 @@ def test_instrumentor(self, request_mock): span = spans_list[0] # Check version and name in span's instrumentation info - # self.assertEqualSpanInstrumentationInfo(span, opentelemetry.instrumentation.elasticsearch) self.assertEqualSpanInstrumentationInfo( span, opentelemetry.instrumentation.elasticsearch ) @@ -475,6 +478,7 @@ def request_hook(span, method, url, kwargs): "headers": { "accept": "application/vnd.elasticsearch+json; compatible-with=8" }, + "otel_span": None, } elif major_version == 7: expected_kwargs = { @@ -607,3 +611,30 @@ def test_bulk(self, request_mock): self.assertEqualSpanInstrumentationInfo( span, opentelemetry.instrumentation.elasticsearch ) + + @mark.skipif( + (major_version, minor_version) < (8, 13), + reason="Native OTel since elasticsearch 8.13", + ) + @mock.patch.dict( + os.environ, + {"OTEL_PYTHON_INSTRUMENTATION_ELASTICSEARCH_ENABLED": "true"}, + ) + def test_instrumentation_is_disabled_if_native_support_enabled( + self, request_mock + ): + request_mock.return_value = helpers.mock_response("{}") + + es = get_elasticsearch_client(hosts=["http://localhost:9200"]) + es.index( + index="sw", + id=1, + **normalize_arguments(body={"name": "adam"}, doc_type="_doc"), + ) + + spans_list = self.get_finished_spans() + self.assertEqual(len(spans_list), 1) + span = spans_list[0] + + # Check that name in span's instrumentation info is not from this instrumentation + self.assertEqual(span.instrumentation_info.name, "elasticsearch-api") diff --git a/tox.ini b/tox.ini index d6fb9eee10..a014306640 100644 --- a/tox.ini +++ b/tox.ini @@ -92,7 +92,7 @@ envlist = ; below mean these dependencies are being used: ; 0: elasticsearch-dsl==6.4.0 elasticsearch==6.8.2 ; 1: elasticsearch-dsl==7.4.1 elasticsearch==7.17.9 - ; 2: elasticsearch-dsl>=8.0,<8.13 elasticsearch>=8.0,<8.13 + ; 2: elasticsearch-dsl==8.13.1 elasticsearch==8.13.1 py3{8,9,10,11}-test-instrumentation-elasticsearch-{0,1,2} pypy3-test-instrumentation-elasticsearch-{0,1,2} lint-instrumentation-elasticsearch From 88111d0a8381bdc440c660b7e802dc951607392a Mon Sep 17 00:00:00 2001 From: Martin Stolle <121817095+stollero@users.noreply.github.com> Date: Tue, 28 May 2024 20:34:28 +0200 Subject: [PATCH 54/58] fix(requests): Fix wrong time unit for duration histogram (#2553) --- CHANGELOG.md | 3 ++- .../src/opentelemetry/instrumentation/requests/__init__.py | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a75e1537cc..7ba8e6d3e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,9 +58,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2524](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2524)) - `opentelemetry-instrumentation-asyncio` Check for __name__ attribute in the coroutine ([#2521](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2521)) +- `opentelemetry-instrumentation-requests` Fix wrong time unit for duration histogram + ([#2553](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2553)) - `opentelemetry-util-http` Preserve brackets around literal IPv6 hosts ([#2552](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2552)) - ## Version 1.24.0/0.45b0 (2024-03-28) ### Added diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index 12797d6f5e..8c54482a46 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -229,9 +229,7 @@ def get_or_create_headers(): exception = exc result = getattr(exc, "response", None) finally: - elapsed_time = max( - round((default_timer() - start_time) * 1000), 0 - ) + elapsed_time = max(default_timer() - start_time, 0) if isinstance(result, Response): span_attributes = {} From 59a737c285f7e3b0e3901781d805f6ecdad41e45 Mon Sep 17 00:00:00 2001 From: Mike Goldsmith Date: Tue, 28 May 2024 19:49:00 +0100 Subject: [PATCH 55/58] Add key predicate to baggage span processor (#2535) * Add key predicate to baggage span processor * add changelog entry * fix linting * more linter fixes --------- Co-authored-by: Diego Hurtado --- CHANGELOG.md | 2 + .../README.rst | 29 +++++++ .../processor/baggage/__init__.py | 4 +- .../processor/baggage/processor.py | 15 +++- .../tests/test_baggage_processor.py | 86 ++++++++++++++++++- 5 files changed, 126 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ba8e6d3e2..6876e2cf9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2397](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2397))) - `opentelemetry-processor-baggage` Initial release ([#2436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2436)) +- `opentelemetry-processor-baggage` Add baggage key predicate + ([#2535](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2535)) ### Fixed diff --git a/processor/opentelemetry-processor-baggage/README.rst b/processor/opentelemetry-processor-baggage/README.rst index 2768758a99..a111166cef 100644 --- a/processor/opentelemetry-processor-baggage/README.rst +++ b/processor/opentelemetry-processor-baggage/README.rst @@ -20,3 +20,32 @@ Do not put sensitive information in Baggage. To repeat: a consequence of adding data to Baggage is that the keys and values will appear in all outgoing HTTP headers from the application. + +## Usage + +Add the span processor when configuring the tracer provider. + +To configure the span processor to copy all baggage entries during configuration: + +```python +from opentelemetry.processor.baggage import BaggageSpanProcessor, ALLOW_ALL_BAGGAGE_KEYS + +tracer_provider = TracerProvider() +tracer_provider.add_span_processor(BaggageSpanProcessor(ALLOW_ALL_BAGGAGE_KEYS)) +``` + +Alternatively, you can provide a custom baggage key predicate to select which baggage keys you want to copy. + +For example, to only copy baggage entries that start with `my-key`: + +```python +starts_with_predicate = lambda baggage_key: baggage_key.startswith("my-key") +tracer_provider.add_span_processor(BaggageSpanProcessor(starts_with_predicate)) +``` + +For example, to only copy baggage entries that match the regex `^key.+`: + +```python +regex_predicate = lambda baggage_key: baggage_key.startswith("^key.+") +tracer_provider.add_span_processor(BaggageSpanProcessor(regex_predicate)) +``` \ No newline at end of file diff --git a/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/__init__.py b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/__init__.py index a740c66491..fcff749d64 100644 --- a/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/__init__.py +++ b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/__init__.py @@ -14,7 +14,7 @@ # pylint: disable=import-error -from .processor import BaggageSpanProcessor +from .processor import ALLOW_ALL_BAGGAGE_KEYS, BaggageSpanProcessor from .version import __version__ -__all__ = ["BaggageSpanProcessor", "__version__"] +__all__ = ["ALLOW_ALL_BAGGAGE_KEYS", "BaggageSpanProcessor", "__version__"] diff --git a/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/processor.py b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/processor.py index 36df06a94c..d14cf3a7e6 100644 --- a/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/processor.py +++ b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/processor.py @@ -12,13 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional +from typing import Callable, Optional from opentelemetry.baggage import get_all as get_all_baggage from opentelemetry.context import Context from opentelemetry.sdk.trace.export import SpanProcessor from opentelemetry.trace import Span +# A BaggageKeyPredicate is a function that takes a baggage key and returns a boolean +BaggageKeyPredicateT = Callable[[str], bool] + +# A BaggageKeyPredicate that always returns True, allowing all baggage keys to be added to spans +ALLOW_ALL_BAGGAGE_KEYS: BaggageKeyPredicateT = lambda _: True + class BaggageSpanProcessor(SpanProcessor): """ @@ -44,12 +50,13 @@ class BaggageSpanProcessor(SpanProcessor): """ - def __init__(self) -> None: - pass + def __init__(self, baggage_key_predicate: BaggageKeyPredicateT) -> None: + self._baggage_key_predicate = baggage_key_predicate def on_start( self, span: "Span", parent_context: Optional[Context] = None ) -> None: baggage = get_all_baggage(parent_context) for key, value in baggage.items(): - span.set_attribute(key, value) + if self._baggage_key_predicate(key): + span.set_attribute(key, value) diff --git a/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py b/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py index 63a71c3cba..fb89ef5eb1 100644 --- a/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py +++ b/processor/opentelemetry-processor-baggage/tests/test_baggage_processor.py @@ -12,12 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +import re import unittest from opentelemetry.baggage import get_all as get_all_baggage from opentelemetry.baggage import set_baggage from opentelemetry.context import attach, detach -from opentelemetry.processor.baggage import BaggageSpanProcessor +from opentelemetry.processor.baggage import ( + ALLOW_ALL_BAGGAGE_KEYS, + BaggageSpanProcessor, +) from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import SpanProcessor from opentelemetry.trace import Span, Tracer @@ -25,13 +29,77 @@ class BaggageSpanProcessorTest(unittest.TestCase): def test_check_the_baggage(self): - self.assertIsInstance(BaggageSpanProcessor(), SpanProcessor) + self.assertIsInstance( + BaggageSpanProcessor(ALLOW_ALL_BAGGAGE_KEYS), SpanProcessor + ) def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_context( self, ): tracer_provider = TracerProvider() - tracer_provider.add_span_processor(BaggageSpanProcessor()) + tracer_provider.add_span_processor( + BaggageSpanProcessor(ALLOW_ALL_BAGGAGE_KEYS) + ) + + # tracer has no baggage to start + tracer = tracer_provider.get_tracer("my-tracer") + self.assertIsInstance(tracer, Tracer) + self.assertEqual(get_all_baggage(), {}) + # set baggage in context + ctx = set_baggage("queen", "bee") + with tracer.start_as_current_span( + name="bumble", context=ctx + ) as bumble_span: + # span should have baggage key-value pair in context + self.assertEqual(get_all_baggage(ctx), {"queen": "bee"}) + # span should have baggage key-value pair in attribute + self.assertEqual(bumble_span._attributes["queen"], "bee") + with tracer.start_as_current_span( + name="child_span", context=ctx + ) as child_span: + self.assertIsInstance(child_span, Span) + # child span should have baggage key-value pair in context + self.assertEqual(get_all_baggage(ctx), {"queen": "bee"}) + # child span should have baggage key-value pair in attribute + self.assertEqual(child_span._attributes["queen"], "bee") + + def test_baggage_span_processor_with_string_prefix( + self, + ): + tracer_provider = TracerProvider() + tracer_provider.add_span_processor( + BaggageSpanProcessor(self.has_prefix) + ) + + # tracer has no baggage to start + tracer = tracer_provider.get_tracer("my-tracer") + self.assertIsInstance(tracer, Tracer) + self.assertEqual(get_all_baggage(), {}) + # set baggage in context + ctx = set_baggage("queen", "bee") + with tracer.start_as_current_span( + name="bumble", context=ctx + ) as bumble_span: + # span should have baggage key-value pair in context + self.assertEqual(get_all_baggage(ctx), {"queen": "bee"}) + # span should have baggage key-value pair in attribute + self.assertEqual(bumble_span._attributes["queen"], "bee") + with tracer.start_as_current_span( + name="child_span", context=ctx + ) as child_span: + self.assertIsInstance(child_span, Span) + # child span should have baggage key-value pair in context + self.assertEqual(get_all_baggage(ctx), {"queen": "bee"}) + # child span should have baggage key-value pair in attribute + self.assertEqual(child_span._attributes["queen"], "bee") + + def test_baggage_span_processor_with_regex( + self, + ): + tracer_provider = TracerProvider() + tracer_provider.add_span_processor( + BaggageSpanProcessor(self.matches_regex) + ) # tracer has no baggage to start tracer = tracer_provider.get_tracer("my-tracer") @@ -59,7 +127,9 @@ def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_token( self, ): tracer_provider = TracerProvider() - tracer_provider.add_span_processor(BaggageSpanProcessor()) + tracer_provider.add_span_processor( + BaggageSpanProcessor(ALLOW_ALL_BAGGAGE_KEYS) + ) # tracer has no baggage to start tracer = tracer_provider.get_tracer("my-tracer") @@ -87,3 +157,11 @@ def test_set_baggage_attaches_to_child_spans_and_detaches_properly_with_token( detach(moar_token) detach(honey_token) self.assertEqual(get_all_baggage(), {}) + + @staticmethod + def has_prefix(baggage_key: str) -> bool: + return baggage_key.startswith("que") + + @staticmethod + def matches_regex(baggage_key: str) -> bool: + return re.match(r"que.*", baggage_key) is not None From ac97b004571474f22e06a46b0e056e48900b607e Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 28 May 2024 23:31:44 +0200 Subject: [PATCH 56/58] Fix typos in test names (#2558) --- .../tests/test_fastapi_instrumentation.py | 2 +- .../tests/test_automatic.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py b/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py index 4269dfa2e4..f9261ad5db 100644 --- a/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py @@ -279,7 +279,7 @@ def test_basic_post_request_metric_success(self): if isinstance(point, NumberDataPoint): self.assertEqual(point.value, 0) - def test_metric_uninstruemnt_app(self): + def test_metric_uninstrument_app(self): self._client.get("/foobar") self._instrumentor.uninstrument_app(self._app) self._client.get("/foobar") diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py index 4715e0b461..7b48e16e17 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py +++ b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py @@ -245,7 +245,7 @@ def test_basic_metric_success(self): ) self.assertEqual(point.value, 0) - def test_metric_uninstruemnt(self): + def test_metric_uninstrument(self): self.client.get("/hello/756") PyramidInstrumentor().uninstrument() self.config = Configurator() From f7cef147390fd39070630ac3730f3a6475a05e34 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 28 May 2024 23:56:38 +0200 Subject: [PATCH 57/58] distro: decouple default configuration test from environment (#2561) --- opentelemetry-distro/tests/test_distro.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/opentelemetry-distro/tests/test_distro.py b/opentelemetry-distro/tests/test_distro.py index dd8f9c4cc4..ea7b9f08c8 100644 --- a/opentelemetry-distro/tests/test_distro.py +++ b/opentelemetry-distro/tests/test_distro.py @@ -14,7 +14,7 @@ # type: ignore import os -from unittest import TestCase +from unittest import TestCase, mock from pkg_resources import DistributionNotFound, require @@ -33,17 +33,10 @@ def test_package_available(self): except DistributionNotFound: self.fail("opentelemetry-distro not installed") + @mock.patch.dict("os.environ", {}, clear=True) def test_default_configuration(self): distro = OpenTelemetryDistro() - self.assertIsNone(os.environ.get(OTEL_TRACES_EXPORTER)) - self.assertIsNone(os.environ.get(OTEL_METRICS_EXPORTER)) distro.configure() - self.assertEqual( - "otlp", os.environ.get(OTEL_TRACES_EXPORTER) - ) - self.assertEqual( - "otlp", os.environ.get(OTEL_METRICS_EXPORTER) - ) - self.assertEqual( - "grpc", os.environ.get(OTEL_EXPORTER_OTLP_PROTOCOL) - ) + self.assertEqual("otlp", os.environ.get(OTEL_TRACES_EXPORTER)) + self.assertEqual("otlp", os.environ.get(OTEL_METRICS_EXPORTER)) + self.assertEqual("grpc", os.environ.get(OTEL_EXPORTER_OTLP_PROTOCOL)) From 25e429aaf98e0eb43ab907b61940cea9c2b86717 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 29 May 2024 01:18:09 +0200 Subject: [PATCH 58/58] instrumentation: revise BaseDistro.load_instrumentor documentation (#2530) --- .../src/opentelemetry/instrumentation/distro.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/distro.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/distro.py index cc1c99c1e0..93646bbb2f 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/distro.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/distro.py @@ -50,9 +50,10 @@ def configure(self, **kwargs): def load_instrumentor( # pylint: disable=no-self-use self, entry_point: EntryPoint, **kwargs ): - """Takes a collection of instrumentation entry points - and activates them by instantiating and calling instrument() - on each one. + """Takes an instrumentation entry point and activates it by instantiating + and calling instrument() on it. + This is called for each opentelemetry_instrumentor entry point by auto + instrumentation. Distros can override this method to customize the behavior by inspecting each entry point and configuring them in special ways,