| 45 | # eventlet 在客户端主动断开时偶尔会抛出 ConnectionAbortedError,这里做一次防御性包裹, |
| 46 | # 避免无意义的堆栈污染日志(仅在 eventlet 可用时启用)。 |
| 47 | def _patch_eventlet_disconnect_logging(): |
| 48 | try: |
| 49 | import eventlet.wsgi # type: ignore |
| 50 | except Exception as exc: # pragma: no cover - 仅在生产环境有效 |
| 51 | logger.debug(f"eventlet 不可用,跳过断开补丁: {exc}") |
| 52 | return |
| 53 | |
| 54 | try: |
| 55 | original_finish = eventlet.wsgi.HttpProtocol.finish # type: ignore[attr-defined] |
| 56 | except Exception as exc: # pragma: no cover |
| 57 | logger.debug(f"eventlet 缺少 HttpProtocol.finish,跳过断开补丁: {exc}") |
| 58 | return |
| 59 | |
| 60 | def _safe_finish(self, *args, **kwargs): # pragma: no cover - 运行时才会触发 |
| 61 | try: |
| 62 | return original_finish(self, *args, **kwargs) |
| 63 | except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError) as exc: |
| 64 | try: |
| 65 | environ = getattr(self, 'environ', {}) or {} |
| 66 | method = environ.get('REQUEST_METHOD', '') |
| 67 | path = environ.get('PATH_INFO', '') |
| 68 | logger.warning(f"客户端已主动断开,忽略异常: {method} {path} ({exc})") |
| 69 | except Exception: |
| 70 | logger.warning(f"客户端已主动断开,忽略异常: {exc}") |
| 71 | return |
| 72 | |
| 73 | eventlet.wsgi.HttpProtocol.finish = _safe_finish # type: ignore[attr-defined] |
| 74 | logger.info("已对 eventlet 连接中断进行安全防护") |
| 75 | |
| 76 | _patch_eventlet_disconnect_logging() |
| 77 | |