From 5661f7ee371020d024e75e7b8de3fd7ebdf8809a Mon Sep 17 00:00:00 2001 From: Vincent Chan Date: Mon, 8 May 2023 11:40:34 -0700 Subject: [PATCH 1/2] Log exception stack traces from executor tasks to API console --- web/server/console.py | 6 ++++++ web/server/main.py | 3 +-- web/server/utils.py | 13 ++++++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/web/server/console.py b/web/server/console.py index 32b40df801..18212ac98c 100644 --- a/web/server/console.py +++ b/web/server/console.py @@ -100,3 +100,9 @@ def log_test_results( def log_success(self, msg: str) -> None: self.queue.put_nowait(self._make_event(msg)) self.stop_snapshot_progress() + + def log_exception(self, tb: str) -> None: + self.queue.put_nowait(self._make_event(tb, event="errors", ok=False)) + + +api_console = ApiConsole() diff --git a/web/server/main.py b/web/server/main.py index 89e3747ca3..d440f0c711 100644 --- a/web/server/main.py +++ b/web/server/main.py @@ -6,11 +6,10 @@ from fastapi.staticfiles import StaticFiles from web.server.api.endpoints import api_router -from web.server.console import ApiConsole +from web.server.console import api_console from web.server.watcher import watch_project app = FastAPI() -api_console = ApiConsole() app.include_router(api_router, prefix="/api") WEB_DIRECTORY = pathlib.Path(__file__).parent.parent diff --git a/web/server/utils.py b/web/server/utils.py index 0ee2ec4081..144210fb59 100644 --- a/web/server/utils.py +++ b/web/server/utils.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +import functools import io import traceback import typing as t @@ -13,6 +14,7 @@ from starlette.status import HTTP_404_NOT_FOUND, HTTP_422_UNPROCESSABLE_ENTITY from sqlmesh.core.context import Context +from web.server.console import api_console from web.server.settings import get_context R = t.TypeVar("R") @@ -26,8 +28,17 @@ def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: async def run_in_executor(func: t.Callable[..., R], *args: t.Any) -> R: """Run in the default loop's executor""" + + @functools.wraps(func) + def func_wrapper() -> R: + try: + return func(*args) + except Exception as e: + api_console.log_exception(traceback.format_exc()) + raise e + loop = asyncio.get_running_loop() - return await loop.run_in_executor(None, func, *args) + return await loop.run_in_executor(None, func_wrapper) def validate_path( From 801c9cc145552a07f6c85dc0ef2a65ffb1ffa453 Mon Sep 17 00:00:00 2001 From: Vincent Chan Date: Mon, 8 May 2023 12:32:25 -0700 Subject: [PATCH 2/2] PR feedback --- web/server/console.py | 10 ++++++++-- web/server/utils.py | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/web/server/console.py b/web/server/console.py index 18212ac98c..320766e138 100644 --- a/web/server/console.py +++ b/web/server/console.py @@ -2,6 +2,7 @@ import asyncio import json +import traceback import typing as t import unittest @@ -101,8 +102,13 @@ def log_success(self, msg: str) -> None: self.queue.put_nowait(self._make_event(msg)) self.stop_snapshot_progress() - def log_exception(self, tb: str) -> None: - self.queue.put_nowait(self._make_event(tb, event="errors", ok=False)) + def log_exception(self, exc: Exception) -> None: + """Log an exception.""" + self.queue.put_nowait( + self._make_event( + {"details": str(exc), "traceback": traceback.format_exc()}, event="errors", ok=False + ) + ) api_console = ApiConsole() diff --git a/web/server/utils.py b/web/server/utils.py index 144210fb59..6cac80ebdd 100644 --- a/web/server/utils.py +++ b/web/server/utils.py @@ -34,7 +34,7 @@ def func_wrapper() -> R: try: return func(*args) except Exception as e: - api_console.log_exception(traceback.format_exc()) + api_console.log_exception(e) raise e loop = asyncio.get_running_loop()