Skip to content

Commit 656a50c

Browse files
Merge branch 'main' into sqlcomment-docs
2 parents 6af9af1 + 6393e84 commit 656a50c

File tree

8 files changed

+96
-4
lines changed

8 files changed

+96
-4
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2121
([#3688](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3688))
2222
- `opentelemetry-instrumentation-tornado` Fix server (request) duration metric calculation
2323
([#3679](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3679))
24+
- `opentelemetry-instrumentation-tornado`: Fix to properly skip all server telemetry when URL excluded.
25+
([#3680](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3680))
2426
- `opentelemetry-instrumentation`: Avoid calls to `context.detach` with `None` token.
2527
([#3673](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3673))
2628
- `opentelemetry-instrumentation-starlette`/`opentelemetry-instrumentation-fastapi`: Fixes a crash when host-based routing is used
@@ -36,6 +38,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3638
([#3666](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3666))
3739
- `opentelemetry-sdk-extension-aws` Add AWS X-Ray Remote Sampler with initial Rules Poller implementation
3840
([#3366](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3366))
41+
- `opentelemetry-instrumentation`: add support for `OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH` to inform opentelemetry-instrument about gevent monkeypatching
42+
([#3699](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3699))
3943

4044
## Version 1.36.0/0.57b0 (2025-07-29)
4145

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -403,11 +403,14 @@ def _wrap(cls, method_name, wrapper):
403403
def _prepare(
404404
tracer, server_histograms, request_hook, func, handler, args, kwargs
405405
):
406-
otel_handler_state = {_START_TIME: default_timer()}
406+
request = handler.request
407+
otel_handler_state = {
408+
_START_TIME: default_timer(),
409+
"exclude_request": _excluded_urls.url_disabled(request.uri),
410+
}
407411
setattr(handler, _HANDLER_STATE_KEY, otel_handler_state)
408412

409-
request = handler.request
410-
if _excluded_urls.url_disabled(request.uri):
413+
if otel_handler_state["exclude_request"]:
411414
return func(*args, **kwargs)
412415

413416
_record_prepare_metrics(server_histograms, handler)
@@ -625,6 +628,8 @@ def _record_prepare_metrics(server_histograms, handler):
625628

626629
def _record_on_finish_metrics(server_histograms, handler, error=None):
627630
otel_handler_state = getattr(handler, _HANDLER_STATE_KEY, None) or {}
631+
if otel_handler_state.get("exclude_request"):
632+
return
628633
start_time = otel_handler_state.get(_START_TIME, None) or default_timer()
629634
elapsed_time = round((default_timer() - start_time) * 1000)
630635

instrumentation/opentelemetry-instrumentation-tornado/tests/test_metrics_instrumentation.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,16 @@ def test_metric_uninstrument(self):
252252
for point in list(metric.data.data_points):
253253
if isinstance(point, HistogramDataPoint):
254254
self.assertEqual(point.count, 1)
255+
256+
def test_exclude_lists(self):
257+
def test_excluded(path):
258+
self.fetch(path)
259+
260+
# Verify no server metrics written (only client ones should exist)
261+
metrics = self.get_sorted_metrics()
262+
for metric in metrics:
263+
self.assertTrue("http.server" not in metric.name, metric)
264+
self.assertEqual(len(metrics), 3, metrics)
265+
266+
test_excluded("/healthz")
267+
test_excluded("/ping")

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: 29 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
@@ -24,6 +26,9 @@
2426
_load_distro,
2527
_load_instrumentors,
2628
)
29+
from opentelemetry.instrumentation.environment_variables import (
30+
OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH,
31+
)
2732
from opentelemetry.instrumentation.utils import _python_path_without_directory
2833
from opentelemetry.instrumentation.version import __version__
2934
from opentelemetry.util._importlib_metadata import entry_points
@@ -130,6 +135,30 @@ def initialize(*, swallow_exceptions: bool = True) -> None:
130135
environ["PYTHONPATH"], dirname(abspath(__file__)), pathsep
131136
)
132137

138+
# handle optional gevent monkey patching. This is done via environment variables so it may be used from the
139+
# opentelemetry operator
140+
gevent_patch: str | None = environ.get(
141+
OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH
142+
)
143+
if gevent_patch is not None:
144+
if gevent_patch != "patch_all":
145+
_logger.error(
146+
"%s value must be `patch_all`",
147+
OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH,
148+
)
149+
else:
150+
try:
151+
# pylint: disable=import-outside-toplevel
152+
from gevent import monkey
153+
154+
getattr(monkey, gevent_patch)()
155+
except ImportError:
156+
_logger.exception(
157+
"Failed to monkey patch with gevent because gevent is not available"
158+
)
159+
if not swallow_exceptions:
160+
raise
161+
133162
try:
134163
distro = _load_distro()
135164
distro.configure()

opentelemetry-instrumentation/src/opentelemetry/instrumentation/environment_variables.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,10 @@
2626
"""
2727
.. envvar:: OTEL_PYTHON_CONFIGURATOR
2828
"""
29+
30+
OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH = (
31+
"OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH"
32+
)
33+
"""
34+
.. envvar:: OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH
35+
"""

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 value 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)