Skip to content

Commit fa86534

Browse files
committed
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 gevent/gevent#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()
1 parent 8c3bf3e commit fa86534

File tree

4 files changed

+60
-1
lines changed

4 files changed

+60
-1
lines changed

opentelemetry-instrumentation/README.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,13 @@ check `here <https://opentelemetry-python.readthedocs.io/en/stable/index.html#in
104104
* ``OTEL_PYTHON_DISABLED_INSTRUMENTATIONS``
105105

106106
If set by the user, opentelemetry-instrument will read this environment variable to disable specific instrumentations.
107-
e.g OTEL_PYTHON_DISABLED_INSTRUMENTATIONS = "requests,django"
107+
e.g OTEL_PYTHON_DISABLED_INSTRUMENTATIONS="requests,django"
108+
109+
* ``OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH``
110+
111+
If set by the user to `patch_all` , opentelemetry instrument will call the gevent monkeypatching method ``patch_all``.
112+
This is considered experimental but can be useful to instrument gevent applications.
113+
e.g OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH=patch_all
108114

109115

110116
Examples

opentelemetry-instrumentation/src/opentelemetry/instrumentation/auto_instrumentation/__init__.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from __future__ import annotations
16+
1517
from argparse import REMAINDER, ArgumentParser
1618
from logging import getLogger
1719
from os import environ, execl, getcwd
@@ -130,6 +132,29 @@ def initialize(*, swallow_exceptions: bool = True) -> None:
130132
environ["PYTHONPATH"], dirname(abspath(__file__)), pathsep
131133
)
132134

135+
# handle optional gevent monkey patching. This is done via environment variables so it may be used from the
136+
# opentelemetry operator
137+
gevent_patch_env_variable_name = (
138+
"OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH"
139+
)
140+
gevent_patch: str | None = environ.get(gevent_patch_env_variable_name)
141+
if gevent_patch is not None:
142+
if gevent_patch != "patch_all":
143+
_logger.error(
144+
"%s values must be `patch_all`", gevent_patch_env_variable_name
145+
)
146+
else:
147+
try:
148+
from gevent import monkey
149+
150+
getattr(monkey, gevent_patch)()
151+
except ImportError:
152+
_logger.error(
153+
"Requested to monkey patch with gevent but gevent is not available"
154+
)
155+
if not swallow_exceptions:
156+
raise
157+
133158
try:
134159
distro = _load_distro()
135160
distro.configure()

opentelemetry-instrumentation/test-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
asgiref==3.8.1
22
Deprecated==1.2.14
3+
gevent==25.5.1
34
iniconfig==2.0.0
45
packaging==24.0
56
pluggy==1.5.0

opentelemetry-instrumentation/tests/auto_instrumentation/test_initialize.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,30 @@ def test_reraises_exceptions(self, load_distro_mock, logger_mock):
7272
)
7373

7474
self.assertEqual("inner exception", str(em.exception))
75+
76+
@patch.dict(
77+
"os.environ",
78+
{
79+
"OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH": "patch_foo"
80+
},
81+
)
82+
@patch("opentelemetry.instrumentation.auto_instrumentation._logger")
83+
def test_handles_invalid_gevent_monkeypatch(self, logger_mock):
84+
# pylint:disable=no-self-use
85+
auto_instrumentation.initialize()
86+
logger_mock.error.assert_called_once_with(
87+
"%s values must be `patch_all`",
88+
"OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH",
89+
)
90+
91+
@patch.dict(
92+
"os.environ",
93+
{
94+
"OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH": "patch_all"
95+
},
96+
)
97+
@patch("opentelemetry.instrumentation.auto_instrumentation._logger")
98+
def test_handles_patch_all_gevent_monkeypatch(self, logger_mock):
99+
# pylint:disable=no-self-use
100+
auto_instrumentation.initialize()
101+
logger_mock.error.assert_not_called()

0 commit comments

Comments
 (0)