|
5 | 5 | from sentry_sdk.consts import OP
|
6 | 6 | from sentry_sdk.integrations import Integration, DidNotEnable
|
7 | 7 | from sentry_sdk.utils import event_from_exception, logger, reraise
|
| 8 | +from sentry_sdk.transport import AsyncHttpTransport |
8 | 9 |
|
9 | 10 | try:
|
10 | 11 | import asyncio
|
@@ -124,3 +125,47 @@ class AsyncioIntegration(Integration):
|
124 | 125 | @staticmethod
|
125 | 126 | def setup_once() -> None:
|
126 | 127 | patch_asyncio()
|
| 128 | + |
| 129 | + def _patch_loop_close() -> None: |
| 130 | + # Atexit shutdown hook happens after the event loop is closed. |
| 131 | + # Therefore, it is necessary to patch the loop.close method to ensure |
| 132 | + # that pending events are flushed before the interpreter shuts down. |
| 133 | + try: |
| 134 | + loop = asyncio.get_running_loop() |
| 135 | + except RuntimeError: |
| 136 | + # No running loop → cannot patch now |
| 137 | + return |
| 138 | + |
| 139 | + if getattr(loop, "_sentry_flush_patched", False): |
| 140 | + return |
| 141 | + |
| 142 | + async def _flush() -> None: |
| 143 | + client = sentry_sdk.get_client() |
| 144 | + if not client: |
| 145 | + return |
| 146 | + try: |
| 147 | + |
| 148 | + if not isinstance(client.transport, AsyncHttpTransport): |
| 149 | + return |
| 150 | + |
| 151 | + t = client.close() |
| 152 | + if t is not None: |
| 153 | + # Wait for the task to complete. |
| 154 | + await t # type: ignore |
| 155 | + except Exception: |
| 156 | + logger.warning( |
| 157 | + "Sentry flush failed during loop shutdown", exc_info=True |
| 158 | + ) |
| 159 | + |
| 160 | + orig_close = loop.close |
| 161 | + |
| 162 | + def _patched_close() -> None: |
| 163 | + try: |
| 164 | + loop.run_until_complete(_flush()) |
| 165 | + finally: |
| 166 | + orig_close() |
| 167 | + |
| 168 | + loop.close = _patched_close |
| 169 | + loop._sentry_flush_patched = True # type: ignore |
| 170 | + |
| 171 | + _patch_loop_close() |
0 commit comments