3131from sentry_sdk .serializer import serialize
3232from sentry_sdk .tracing import trace
3333from sentry_sdk .tracing_utils import has_span_streaming_enabled
34- from sentry_sdk .transport import BaseHttpTransport , make_transport
34+ from sentry_sdk .transport import (
35+ HttpTransportCore ,
36+ make_transport ,
37+ AsyncHttpTransport ,
38+ )
3539from sentry_sdk .consts import (
3640 SPANDATA ,
3741 DEFAULT_MAX_VALUE_LENGTH ,
@@ -251,6 +255,12 @@ def close(self, *args: "Any", **kwargs: "Any") -> None:
251255 def flush (self , * args : "Any" , ** kwargs : "Any" ) -> None :
252256 return None
253257
258+ async def close_async (self , * args : "Any" , ** kwargs : "Any" ) -> None :
259+ return None
260+
261+ async def flush_async (self , * args : "Any" , ** kwargs : "Any" ) -> None :
262+ return None
263+
254264 def __enter__ (self ) -> "BaseClient" :
255265 return self
256266
@@ -472,7 +482,7 @@ def _record_lost_event(
472482 or self .metrics_batcher
473483 or self .span_batcher
474484 or has_profiling_enabled (self .options )
475- or isinstance (self .transport , BaseHttpTransport )
485+ or isinstance (self .transport , HttpTransportCore )
476486 ):
477487 # If we have anything on that could spawn a background thread, we
478488 # need to check if it's safe to use them.
@@ -999,6 +1009,32 @@ def get_integration(
9991009
10001010 return self .integrations .get (integration_name )
10011011
1012+ def _has_async_transport (self ) -> bool :
1013+ """Check if the current transport is async."""
1014+ return isinstance (self .transport , AsyncHttpTransport )
1015+
1016+ @property
1017+ def _batchers (self ) -> "tuple[Any, ...]" :
1018+ return tuple (
1019+ b
1020+ for b in (self .log_batcher , self .metrics_batcher , self .span_batcher )
1021+ if b is not None
1022+ )
1023+
1024+ def _close_components (self ) -> None :
1025+ """Kill all client components in the correct order."""
1026+ self .session_flusher .kill ()
1027+ for b in self ._batchers :
1028+ b .kill ()
1029+ if self .monitor :
1030+ self .monitor .kill ()
1031+
1032+ def _flush_components (self ) -> None :
1033+ """Flush all client components."""
1034+ self .session_flusher .flush ()
1035+ for b in self ._batchers :
1036+ b .flush ()
1037+
10021038 def close (
10031039 self ,
10041040 timeout : "Optional[float]" = None ,
@@ -1009,19 +1045,40 @@ def close(
10091045 semantics as :py:meth:`Client.flush`.
10101046 """
10111047 if self .transport is not None :
1012- self .flush (timeout = timeout , callback = callback )
1013- self .session_flusher .kill ()
1014- if self .log_batcher is not None :
1015- self .log_batcher .kill ()
1016- if self .metrics_batcher is not None :
1017- self .metrics_batcher .kill ()
1018- if self .span_batcher is not None :
1019- self .span_batcher .kill ()
1020- if self .monitor :
1021- self .monitor .kill ()
1048+ if self ._has_async_transport ():
1049+ warnings .warn (
1050+ "close() used with AsyncHttpTransport. Use close_async() instead." ,
1051+ stacklevel = 2 ,
1052+ )
1053+ self ._flush_components ()
1054+ else :
1055+ self .flush (timeout = timeout , callback = callback )
1056+ self ._close_components ()
10221057 self .transport .kill ()
10231058 self .transport = None
10241059
1060+ async def close_async (
1061+ self ,
1062+ timeout : "Optional[float]" = None ,
1063+ callback : "Optional[Callable[[int, float], None]]" = None ,
1064+ ) -> None :
1065+ """
1066+ Asynchronously close the client and shut down the transport. Arguments have the same
1067+ semantics as :py:meth:`Client.flush_async`.
1068+ """
1069+ if self .transport is not None :
1070+ if not self ._has_async_transport ():
1071+ logger .debug (
1072+ "close_async() used with non-async transport, aborting. Please use close() instead."
1073+ )
1074+ return
1075+ await self .flush_async (timeout = timeout , callback = callback )
1076+ self ._close_components ()
1077+ kill_task = self .transport .kill () # type: ignore
1078+ if kill_task is not None :
1079+ await kill_task
1080+ self .transport = None
1081+
10251082 def flush (
10261083 self ,
10271084 timeout : "Optional[float]" = None ,
@@ -1035,23 +1092,55 @@ def flush(
10351092 :param callback: Is invoked with the number of pending events and the configured timeout.
10361093 """
10371094 if self .transport is not None :
1095+ if self ._has_async_transport ():
1096+ warnings .warn (
1097+ "flush() used with AsyncHttpTransport. Use flush_async() instead." ,
1098+ stacklevel = 2 ,
1099+ )
1100+ return
10381101 if timeout is None :
10391102 timeout = self .options ["shutdown_timeout" ]
1040- self .session_flusher .flush ()
1041- if self .log_batcher is not None :
1042- self .log_batcher .flush ()
1043- if self .metrics_batcher is not None :
1044- self .metrics_batcher .flush ()
1045- if self .span_batcher is not None :
1046- self .span_batcher .flush ()
1103+ self ._flush_components ()
1104+
10471105 self .transport .flush (timeout = timeout , callback = callback )
10481106
1107+ async def flush_async (
1108+ self ,
1109+ timeout : "Optional[float]" = None ,
1110+ callback : "Optional[Callable[[int, float], None]]" = None ,
1111+ ) -> None :
1112+ """
1113+ Asynchronously wait for the current events to be sent.
1114+
1115+ :param timeout: Wait for at most `timeout` seconds. If no `timeout` is provided, the `shutdown_timeout` option value is used.
1116+
1117+ :param callback: Is invoked with the number of pending events and the configured timeout.
1118+ """
1119+ if self .transport is not None :
1120+ if not self ._has_async_transport ():
1121+ logger .debug (
1122+ "flush_async() used with non-async transport, aborting. Please use flush() instead."
1123+ )
1124+ return
1125+ if timeout is None :
1126+ timeout = self .options ["shutdown_timeout" ]
1127+ self ._flush_components ()
1128+ flush_task = self .transport .flush (timeout = timeout , callback = callback ) # type: ignore
1129+ if flush_task is not None :
1130+ await flush_task
1131+
10491132 def __enter__ (self ) -> "_Client" :
10501133 return self
10511134
10521135 def __exit__ (self , exc_type : "Any" , exc_value : "Any" , tb : "Any" ) -> None :
10531136 self .close ()
10541137
1138+ async def __aenter__ (self ) -> "_Client" :
1139+ return self
1140+
1141+ async def __aexit__ (self , exc_type : "Any" , exc_value : "Any" , tb : "Any" ) -> None :
1142+ await self .close_async ()
1143+
10551144
10561145from typing import TYPE_CHECKING
10571146
0 commit comments