diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c0f788197..2483db9db2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#3666](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3666)) - `opentelemetry-sdk-extension-aws` Add AWS X-Ray Remote Sampler with initial Rules Poller implementation ([#3366](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3366)) +- `opentelemetry-instrumentation`: add support for `OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH` to inform opentelemetry-instrument about gevent monkeypatching + ([#3699](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3699)) ## Version 1.36.0/0.57b0 (2025-07-29) diff --git a/opentelemetry-instrumentation/README.rst b/opentelemetry-instrumentation/README.rst index c8731fda2e..a342aa188c 100644 --- a/opentelemetry-instrumentation/README.rst +++ b/opentelemetry-instrumentation/README.rst @@ -104,7 +104,13 @@ check `here None: environ["PYTHONPATH"], dirname(abspath(__file__)), pathsep ) + # handle optional gevent monkey patching. This is done via environment variables so it may be used from the + # opentelemetry operator + gevent_patch: str | None = environ.get( + OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH + ) + if gevent_patch is not None: + if gevent_patch != "patch_all": + _logger.error( + "%s value must be `patch_all`", + OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH, + ) + else: + try: + # pylint: disable=import-outside-toplevel + from gevent import monkey + + getattr(monkey, gevent_patch)() + except ImportError: + _logger.exception( + "Failed to monkey patch with gevent because gevent is not available" + ) + if not swallow_exceptions: + raise + try: distro = _load_distro() distro.configure() diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/environment_variables.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/environment_variables.py index 7886779632..ee95bb3587 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/environment_variables.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/environment_variables.py @@ -26,3 +26,10 @@ """ .. envvar:: OTEL_PYTHON_CONFIGURATOR """ + +OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH = ( + "OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH" +) +""" +.. envvar:: OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH +""" diff --git a/opentelemetry-instrumentation/test-requirements.txt b/opentelemetry-instrumentation/test-requirements.txt index 943a45c8f4..58c09a950b 100644 --- a/opentelemetry-instrumentation/test-requirements.txt +++ b/opentelemetry-instrumentation/test-requirements.txt @@ -1,5 +1,6 @@ asgiref==3.8.1 Deprecated==1.2.14 +gevent==25.5.1 iniconfig==2.0.0 packaging==24.0 pluggy==1.5.0 diff --git a/opentelemetry-instrumentation/tests/auto_instrumentation/test_initialize.py b/opentelemetry-instrumentation/tests/auto_instrumentation/test_initialize.py index 715da0d2f6..c2bf7116d9 100644 --- a/opentelemetry-instrumentation/tests/auto_instrumentation/test_initialize.py +++ b/opentelemetry-instrumentation/tests/auto_instrumentation/test_initialize.py @@ -72,3 +72,30 @@ def test_reraises_exceptions(self, load_distro_mock, logger_mock): ) self.assertEqual("inner exception", str(em.exception)) + + @patch.dict( + "os.environ", + { + "OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH": "patch_foo" + }, + ) + @patch("opentelemetry.instrumentation.auto_instrumentation._logger") + def test_handles_invalid_gevent_monkeypatch(self, logger_mock): + # pylint:disable=no-self-use + auto_instrumentation.initialize() + logger_mock.error.assert_called_once_with( + "%s value must be `patch_all`", + "OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH", + ) + + @patch.dict( + "os.environ", + { + "OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH": "patch_all" + }, + ) + @patch("opentelemetry.instrumentation.auto_instrumentation._logger") + def test_handles_patch_all_gevent_monkeypatch(self, logger_mock): + # pylint:disable=no-self-use + auto_instrumentation.initialize() + logger_mock.error.assert_not_called()