diff --git a/src/dstack/_internal/server/app.py b/src/dstack/_internal/server/app.py index 736733b40..9c83bac79 100644 --- a/src/dstack/_internal/server/app.py +++ b/src/dstack/_internal/server/app.py @@ -58,6 +58,7 @@ SERVER_URL, UPDATE_DEFAULT_PROJECT, ) +from dstack._internal.server.utils import sentry_utils from dstack._internal.server.utils.logging import configure_logging from dstack._internal.server.utils.routers import ( CustomORJSONResponse, @@ -105,6 +106,7 @@ async def lifespan(app: FastAPI): enable_tracing=True, traces_sampler=_sentry_traces_sampler, profiles_sample_rate=settings.SENTRY_PROFILES_SAMPLE_RATE, + before_send=sentry_utils.AsyncioCancelledErrorFilterEventProcessor(), ) server_executor = ThreadPoolExecutor(max_workers=settings.SERVER_EXECUTOR_MAX_WORKERS) asyncio.get_running_loop().set_default_executor(server_executor) diff --git a/src/dstack/_internal/server/utils/sentry_utils.py b/src/dstack/_internal/server/utils/sentry_utils.py index c878e1e91..8dd7326b7 100644 --- a/src/dstack/_internal/server/utils/sentry_utils.py +++ b/src/dstack/_internal/server/utils/sentry_utils.py @@ -1,6 +1,9 @@ +import asyncio import functools +from typing import Optional import sentry_sdk +from sentry_sdk.types import Event, Hint def instrument_background_task(f): @@ -10,3 +13,12 @@ async def wrapper(*args, **kwargs): return await f(*args, **kwargs) return wrapper + + +class AsyncioCancelledErrorFilterEventProcessor: + # See https://docs.sentry.io/platforms/python/configuration/filtering/#filtering-error-events + def __call__(self, event: Event, hint: Hint) -> Optional[Event]: + exc_info = hint.get("exc_info") + if exc_info and isinstance(exc_info[1], asyncio.CancelledError): + return None + return event