|
59 | 59 | _FUNCTION_STATUS_HEADER_FIELD = "X-Google-Status" |
60 | 60 | _CRASH = "crash" |
61 | 61 |
|
62 | | - |
63 | | -async def _crash_handler(request, exc): |
64 | | - logger = logging.getLogger() |
65 | | - tb_lines = traceback.format_exception(type(exc), exc, exc.__traceback__) |
66 | | - tb_text = "".join(tb_lines) |
67 | | - error_msg = ( |
68 | | - f"Exception on {request.url.path} [{request.method}]\n{tb_text}".rstrip() |
69 | | - ) |
70 | | - |
71 | | - logger.error(error_msg) |
72 | | - |
73 | | - headers = {_FUNCTION_STATUS_HEADER_FIELD: _CRASH} |
74 | | - return Response("Internal Server Error", status_code=500, headers=headers) |
75 | | - |
76 | | - |
77 | 62 | CloudEventFunction = Callable[[CloudEvent], Union[None, Awaitable[None]]] |
78 | 63 | HTTPFunction = Callable[[Request], Union[HTTPResponse, Awaitable[HTTPResponse]]] |
79 | 64 |
|
@@ -193,6 +178,45 @@ def _configure_app_execution_id_logging(): |
193 | 178 | ) |
194 | 179 |
|
195 | 180 |
|
| 181 | + |
| 182 | + |
| 183 | +class ExceptionHandlerMiddleware: |
| 184 | + def __init__(self, app): |
| 185 | + self.app = app |
| 186 | + |
| 187 | + async def __call__(self, scope, receive, send): |
| 188 | + if scope["type"] != "http": # pragma: no cover |
| 189 | + await self.app(scope, receive, send) |
| 190 | + return |
| 191 | + |
| 192 | + try: |
| 193 | + await self.app(scope, receive, send) |
| 194 | + except Exception as exc: |
| 195 | + logger = logging.getLogger() |
| 196 | + tb_lines = traceback.format_exception(type(exc), exc, exc.__traceback__) |
| 197 | + tb_text = "".join(tb_lines) |
| 198 | + |
| 199 | + path = scope.get("path", "/") |
| 200 | + method = scope.get("method", "GET") |
| 201 | + error_msg = f"Exception on {path} [{method}]\n{tb_text}".rstrip() |
| 202 | + |
| 203 | + logger.error(error_msg) |
| 204 | + |
| 205 | + headers = [[b"content-type", b"text/plain"], |
| 206 | + [_FUNCTION_STATUS_HEADER_FIELD.encode(), _CRASH.encode()]] |
| 207 | + |
| 208 | + await send({ |
| 209 | + "type": "http.response.start", |
| 210 | + "status": 500, |
| 211 | + "headers": headers, |
| 212 | + }) |
| 213 | + await send({ |
| 214 | + "type": "http.response.body", |
| 215 | + "body": b"Internal Server Error", |
| 216 | + }) |
| 217 | + # Don't re-raise to prevent starlette from printing tracebak again |
| 218 | + |
| 219 | + |
196 | 220 | def create_asgi_app(target=None, source=None, signature_type=None): |
197 | 221 | """Create an ASGI application for the function. |
198 | 222 |
|
@@ -267,13 +291,16 @@ def create_asgi_app(target=None, source=None, signature_type=None): |
267 | 291 | f"Unsupported signature type for ASGI server: {signature_type}" |
268 | 292 | ) |
269 | 293 |
|
270 | | - exception_handlers = { |
271 | | - Exception: _crash_handler, |
272 | | - } |
273 | | - app = Starlette(routes=routes, exception_handlers=exception_handlers) |
274 | | - |
275 | | - # Apply ASGI middleware for execution ID |
276 | | - app = execution_id.AsgiMiddleware(app) |
| 294 | + from starlette.middleware import Middleware |
| 295 | + |
| 296 | + app = Starlette( |
| 297 | + debug=False, |
| 298 | + routes=routes, |
| 299 | + middleware=[ |
| 300 | + Middleware(ExceptionHandlerMiddleware), |
| 301 | + Middleware(execution_id.AsgiMiddleware), |
| 302 | + ], |
| 303 | + ) |
277 | 304 |
|
278 | 305 | return app |
279 | 306 |
|
|
0 commit comments