Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions elasticapm/context/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,40 @@
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


def init_execution_context():
# If _threading_local has been monkeypatched (by gevent or eventlet), then
# we should assume it's use as this will be the most "green-thread safe"
if threading_local_monkey_patched():
from elasticapm.context.threadlocal import execution_context

return execution_context

try:
from elasticapm.context.contextvars import execution_context
except ImportError:
from elasticapm.context.threadlocal import execution_context
return execution_context


def threading_local_monkey_patched():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we're at it, it might make sense to do the same check for eventlet

http://eventlet.net/doc/patching.html#eventlet.patcher.is_monkey_patched

# Returns True if thread locals have been patched by either gevent of
# eventlet
try:
from gevent.monkey import is_object_patched
except ImportError:
pass
else:
if is_object_patched("_threading", "local"):
return True

try:
from eventlet.patcher import is_monkey_patched
except ImportError:
pass
else:
if is_monkey_patched("thread"):
return True

return False
6 changes: 2 additions & 4 deletions elasticapm/traces.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

from elasticapm.conf import constants
from elasticapm.conf.constants import SPAN, TRANSACTION
from elasticapm.context import init_execution_context
from elasticapm.utils import compat, encoding, get_name_from_func
from elasticapm.utils.disttracing import TraceParent, TracingOptions

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


try:
from elasticapm.context.contextvars import execution_context
except ImportError:
from elasticapm.context.threadlocal import execution_context
execution_context = init_execution_context()


class Transaction(object):
Expand Down
Empty file added tests/context/__init__.py
Empty file.
24 changes: 24 additions & 0 deletions tests/context/test_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import sys

import elasticapm.context
from elasticapm.context.threadlocal import ThreadLocalContext


def test_execution_context_backing():
execution_context = elasticapm.context.init_execution_context()

if sys.version_info[0] == 3 and sys.version_info[1] >= 7:
from elasticapm.context.contextvars import ContextVarsContext

assert isinstance(execution_context, ContextVarsContext)
else:
assert isinstance(execution_context, ThreadLocalContext)


def test_execution_context_monkeypatched(monkeypatch):
with monkeypatch.context() as m:
m.setattr(elasticapm.context, "threading_local_monkey_patched", lambda: True)
execution_context = elasticapm.context.init_execution_context()

# Should always use ThreadLocalContext when thread local is monkey patched
assert isinstance(execution_context, ThreadLocalContext)