Skip to content

Commit c935e9e

Browse files
committed
ref(transport): Fix event loop handling in async transport
Async Transport now properly checks for the presence of the event loop in capture_envelop, and drops items in case the event loop is no longer running for some reason. GH-4582
1 parent a644465 commit c935e9e

File tree

1 file changed

+25
-14
lines changed

1 file changed

+25
-14
lines changed

sentry_sdk/transport.py

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
from sentry_sdk.consts import EndpointType
3131
from sentry_sdk.utils import Dsn, logger, capture_internal_exceptions
32-
from sentry_sdk.worker import BackgroundWorker, Worker
32+
from sentry_sdk.worker import BackgroundWorker, Worker, AsyncWorker
3333
from sentry_sdk.envelope import Envelope, Item, PayloadRef
3434

3535
from typing import TYPE_CHECKING
@@ -225,9 +225,10 @@ def __init__(self: Self, options: Dict[str, Any]) -> None:
225225
elif self._compression_algo == "br":
226226
self._compression_level = 4
227227

228-
def _create_worker(self: Self, options: Dict[str, Any]) -> Worker:
229-
# For now, we only support the threaded sync background worker.
230-
return BackgroundWorker(queue_size=options["transport_queue_size"])
228+
def _create_worker(self, options: dict[str, Any]) -> Worker:
229+
async_enabled = options.get("_experiments", {}).get("transport_async", False)
230+
worker_cls = AsyncWorker if async_enabled else BackgroundWorker
231+
return worker_cls(queue_size=options["transport_queue_size"])
231232

232233
def record_lost_event(
233234
self: Self,
@@ -647,18 +648,26 @@ async def send_envelope_wrapper() -> None:
647648

648649
def capture_envelope(self: Self, envelope: Envelope) -> None:
649650
# Synchronous entry point
650-
if asyncio.get_running_loop() is not None:
651+
try:
652+
asyncio.get_running_loop()
651653
# We are on the main thread running the event loop
652654
task = asyncio.create_task(self._capture_envelope(envelope))
653655
self.background_tasks.add(task)
654656
task.add_done_callback(self.background_tasks.discard)
655-
else:
657+
except RuntimeError:
656658
# We are in a background thread, not running an event loop,
657659
# have to launch the task on the loop in a threadsafe way.
658-
asyncio.run_coroutine_threadsafe(
659-
self._capture_envelope(envelope),
660-
self._loop,
661-
)
660+
if self._loop and self._loop.is_running():
661+
asyncio.run_coroutine_threadsafe(
662+
self._capture_envelope(envelope),
663+
self._loop,
664+
)
665+
else:
666+
# The event loop is no longer running
667+
logger.warning("Async Transport is not running in an event loop.")
668+
self.on_dropped_event("no_async_context")
669+
for item in envelope.items:
670+
self.record_lost_event("no_async_context", item=item)
662671

663672
async def flush_async(
664673
self: Self,
@@ -998,11 +1007,13 @@ def make_transport(options: Dict[str, Any]) -> Optional[Transport]:
9981007
ref_transport = options["transport"]
9991008

10001009
use_http2_transport = options.get("_experiments", {}).get("transport_http2", False)
1001-
1010+
use_async_transport = options.get("_experiments", {}).get("transport_async", False)
10021011
# By default, we use the http transport class
1003-
transport_cls: Type[Transport] = (
1004-
Http2Transport if use_http2_transport else HttpTransport
1005-
)
1012+
if use_async_transport and asyncio.get_running_loop() is not None:
1013+
transport_cls: Type[Transport] = AsyncHttpTransport
1014+
else:
1015+
use_http2 = use_http2_transport
1016+
transport_cls = Http2Transport if use_http2 else HttpTransport
10061017

10071018
if isinstance(ref_transport, Transport):
10081019
return ref_transport

0 commit comments

Comments
 (0)