Skip to content

Commit 090b938

Browse files
ramshaw888beniwohli
authored andcommitted
Execution context - use thread locals if gevent/eventlet has monkey patched it (#454)
* Execution context - use thread locals if gevent/eventlet has monkey patched it #453 * Extract out logic to decide which backing to use for execution_context into a function init_execution_context * Check if gevent or eventlet has monkey patched _threading.local, if it has then use elasticapm.context.threadlocal as the backing. closes #451
1 parent 5337c43 commit 090b938

File tree

4 files changed

+123
-4
lines changed

4 files changed

+123
-4
lines changed

elasticapm/context/__init__.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,40 @@
2727
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2828
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2929
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+
31+
32+
def init_execution_context():
33+
# If _threading_local has been monkeypatched (by gevent or eventlet), then
34+
# we should assume it's use as this will be the most "green-thread safe"
35+
if threading_local_monkey_patched():
36+
from elasticapm.context.threadlocal import execution_context
37+
38+
return execution_context
39+
40+
try:
41+
from elasticapm.context.contextvars import execution_context
42+
except ImportError:
43+
from elasticapm.context.threadlocal import execution_context
44+
return execution_context
45+
46+
47+
def threading_local_monkey_patched():
48+
# Returns True if thread locals have been patched by either gevent of
49+
# eventlet
50+
try:
51+
from gevent.monkey import is_object_patched
52+
except ImportError:
53+
pass
54+
else:
55+
if is_object_patched("_threading", "local"):
56+
return True
57+
58+
try:
59+
from eventlet.patcher import is_monkey_patched
60+
except ImportError:
61+
pass
62+
else:
63+
if is_monkey_patched("thread"):
64+
return True
65+
66+
return False

elasticapm/traces.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
from elasticapm.conf import constants
3939
from elasticapm.conf.constants import SPAN, TRANSACTION
40+
from elasticapm.context import init_execution_context
4041
from elasticapm.utils import compat, encoding, get_name_from_func
4142
from elasticapm.utils.disttracing import TraceParent, TracingOptions
4243

@@ -51,10 +52,7 @@
5152
TAG_RE = re.compile('[.*"]')
5253

5354

54-
try:
55-
from elasticapm.context.contextvars import execution_context
56-
except ImportError:
57-
from elasticapm.context.threadlocal import execution_context
55+
execution_context = init_execution_context()
5856

5957

6058
class Transaction(object):

tests/context/__init__.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# BSD 3-Clause License
2+
#
3+
# Copyright (c) 2019, Elasticsearch BV
4+
# All rights reserved.
5+
#
6+
# Redistribution and use in source and binary forms, with or without
7+
# modification, are permitted provided that the following conditions are met:
8+
#
9+
# * Redistributions of source code must retain the above copyright notice, this
10+
# list of conditions and the following disclaimer.
11+
#
12+
# * Redistributions in binary form must reproduce the above copyright notice,
13+
# this list of conditions and the following disclaimer in the documentation
14+
# and/or other materials provided with the distribution.
15+
#
16+
# * Neither the name of the copyright holder nor the names of its
17+
# contributors may be used to endorse or promote products derived from
18+
# this software without specific prior written permission.
19+
#
20+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

tests/context/test_context.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# BSD 3-Clause License
2+
#
3+
# Copyright (c) 2019, Elasticsearch BV
4+
# All rights reserved.
5+
#
6+
# Redistribution and use in source and binary forms, with or without
7+
# modification, are permitted provided that the following conditions are met:
8+
#
9+
# * Redistributions of source code must retain the above copyright notice, this
10+
# list of conditions and the following disclaimer.
11+
#
12+
# * Redistributions in binary form must reproduce the above copyright notice,
13+
# this list of conditions and the following disclaimer in the documentation
14+
# and/or other materials provided with the distribution.
15+
#
16+
# * Neither the name of the copyright holder nor the names of its
17+
# contributors may be used to endorse or promote products derived from
18+
# this software without specific prior written permission.
19+
#
20+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+
31+
32+
import sys
33+
34+
import elasticapm.context
35+
from elasticapm.context.threadlocal import ThreadLocalContext
36+
37+
38+
def test_execution_context_backing():
39+
execution_context = elasticapm.context.init_execution_context()
40+
41+
if sys.version_info[0] == 3 and sys.version_info[1] >= 7:
42+
from elasticapm.context.contextvars import ContextVarsContext
43+
44+
assert isinstance(execution_context, ContextVarsContext)
45+
else:
46+
assert isinstance(execution_context, ThreadLocalContext)
47+
48+
49+
def test_execution_context_monkeypatched(monkeypatch):
50+
with monkeypatch.context() as m:
51+
m.setattr(elasticapm.context, "threading_local_monkey_patched", lambda: True)
52+
execution_context = elasticapm.context.init_execution_context()
53+
54+
# Should always use ThreadLocalContext when thread local is monkey patched
55+
assert isinstance(execution_context, ThreadLocalContext)

0 commit comments

Comments
 (0)