opentelemetry-instrumentation: teach opentelemetry-instrument about gevent (#3699)
* opentelemetry-instrumentation: teach opentelemetry-instrument about gevent Introduce OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH=patch_all environment variable that calls gevent monkey module patch_all method before starting up the distro and sdk. The environment variable should useful also for apps instrumented via the opentelemetry-operator. The flag removes the following warning (and hang) when running locust: $ opentelemetry-instrument locust /lib/python3.10/site-packages/locust/__init__.py:16: MonkeyPatchWarning: Monkey-patching ssl after ssl has already been imported may lead to errors, including RecursionError on Python 3.6. It may also silently lead to incorrect behaviour on Python 3.7. Please monkey-patch earlier. See https://github.com/gevent/gevent/issues/1016. Modules that had direct imports (NOT patched): ['urllib3.util (/lib/python3.10/site-packages/urllib3/util/__init__.py)', 'urllib3.util.ssl_ (/lib/python3.10/site-packages/urllib3/util/ssl_.py)']. monkey.patch_all() * Update CHANGELOG * Please pylint * Apply suggestions from code review Co-authored-by: Tammy Baylis <96076570+tammy-baylis-swi@users.noreply.github.com> * Move environment variable to proper module --------- Co-authored-by: Tammy Baylis <96076570+tammy-baylis-swi@users.noreply.github.com>
This commit is contained in:
parent
86d26ce1b8
commit
5f0d4ff4ce
|
|
@ -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))
|
([#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
|
- `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))
|
([#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)
|
## Version 1.36.0/0.57b0 (2025-07-29)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,13 @@ check `here <https://opentelemetry-python.readthedocs.io/en/stable/index.html#in
|
||||||
* ``OTEL_PYTHON_DISABLED_INSTRUMENTATIONS``
|
* ``OTEL_PYTHON_DISABLED_INSTRUMENTATIONS``
|
||||||
|
|
||||||
If set by the user, opentelemetry-instrument will read this environment variable to disable specific instrumentations.
|
If set by the user, opentelemetry-instrument will read this environment variable to disable specific instrumentations.
|
||||||
e.g OTEL_PYTHON_DISABLED_INSTRUMENTATIONS = "requests,django"
|
e.g OTEL_PYTHON_DISABLED_INSTRUMENTATIONS="requests,django"
|
||||||
|
|
||||||
|
* ``OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH``
|
||||||
|
|
||||||
|
If set by the user to `patch_all` , opentelemetry instrument will call the gevent monkeypatching method ``patch_all``.
|
||||||
|
This is considered experimental but can be useful to instrument gevent applications.
|
||||||
|
e.g OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH=patch_all
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from argparse import REMAINDER, ArgumentParser
|
from argparse import REMAINDER, ArgumentParser
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from os import environ, execl, getcwd
|
from os import environ, execl, getcwd
|
||||||
|
|
@ -24,6 +26,9 @@ from opentelemetry.instrumentation.auto_instrumentation._load import (
|
||||||
_load_distro,
|
_load_distro,
|
||||||
_load_instrumentors,
|
_load_instrumentors,
|
||||||
)
|
)
|
||||||
|
from opentelemetry.instrumentation.environment_variables import (
|
||||||
|
OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH,
|
||||||
|
)
|
||||||
from opentelemetry.instrumentation.utils import _python_path_without_directory
|
from opentelemetry.instrumentation.utils import _python_path_without_directory
|
||||||
from opentelemetry.instrumentation.version import __version__
|
from opentelemetry.instrumentation.version import __version__
|
||||||
from opentelemetry.util._importlib_metadata import entry_points
|
from opentelemetry.util._importlib_metadata import entry_points
|
||||||
|
|
@ -130,6 +135,30 @@ def initialize(*, swallow_exceptions: bool = True) -> None:
|
||||||
environ["PYTHONPATH"], dirname(abspath(__file__)), pathsep
|
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:
|
try:
|
||||||
distro = _load_distro()
|
distro = _load_distro()
|
||||||
distro.configure()
|
distro.configure()
|
||||||
|
|
|
||||||
|
|
@ -26,3 +26,10 @@ OTEL_PYTHON_CONFIGURATOR = "OTEL_PYTHON_CONFIGURATOR"
|
||||||
"""
|
"""
|
||||||
.. envvar:: OTEL_PYTHON_CONFIGURATOR
|
.. 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
|
||||||
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
asgiref==3.8.1
|
asgiref==3.8.1
|
||||||
Deprecated==1.2.14
|
Deprecated==1.2.14
|
||||||
|
gevent==25.5.1
|
||||||
iniconfig==2.0.0
|
iniconfig==2.0.0
|
||||||
packaging==24.0
|
packaging==24.0
|
||||||
pluggy==1.5.0
|
pluggy==1.5.0
|
||||||
|
|
|
||||||
|
|
@ -72,3 +72,30 @@ class TestInitialize(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual("inner exception", str(em.exception))
|
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()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue