diff --git a/packages/service-library/src/servicelib/aiohttp/rest_middlewares.py b/packages/service-library/src/servicelib/aiohttp/rest_middlewares.py index 1737ee012344..ddf10e2fcf8e 100644 --- a/packages/service-library/src/servicelib/aiohttp/rest_middlewares.py +++ b/packages/service-library/src/servicelib/aiohttp/rest_middlewares.py @@ -19,7 +19,7 @@ from servicelib.rest_constants import RESPONSE_MODEL_POLICY from servicelib.status_codes_utils import is_5xx_server_error -from ..logging_errors import create_troubleshotting_log_kwargs +from ..logging_errors import create_troubleshootting_log_kwargs from ..mimetype_constants import MIMETYPE_APPLICATION_JSON from ..rest_responses import is_enveloped_from_map, is_enveloped_from_text from ..status_codes_utils import get_code_description @@ -72,7 +72,7 @@ def _log_5xx_server_error( error_code, error_context = _create_error_context(request, exception) _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_error_msg, error=exception, error_context=error_context, diff --git a/packages/service-library/src/servicelib/background_task_utils.py b/packages/service-library/src/servicelib/background_task_utils.py index 8313f6424303..1564107420dc 100644 --- a/packages/service-library/src/servicelib/background_task_utils.py +++ b/packages/service-library/src/servicelib/background_task_utils.py @@ -3,7 +3,7 @@ from collections.abc import Callable, Coroutine from typing import Any, ParamSpec, TypeVar -from servicelib.exception_utils import silence_exceptions +from servicelib.exception_utils import suppress_exceptions from servicelib.redis._errors import CouldNotAcquireLockError from .background_task import periodic @@ -39,10 +39,11 @@ def _decorator( coro: Callable[P, Coroutine[Any, Any, None]], ) -> Callable[P, Coroutine[Any, Any, None]]: @periodic(interval=retry_after) - @silence_exceptions( + @suppress_exceptions( # Replicas will raise CouldNotAcquireLockError # SEE https://github.com/ITISFoundation/osparc-simcore/issues/7574 - (CouldNotAcquireLockError,) + (CouldNotAcquireLockError,), + reason="Multiple instances of the periodic task `{coro.__module__}.{coro.__name__}` are running.", ) @exclusive( redis_client, diff --git a/packages/service-library/src/servicelib/exception_utils.py b/packages/service-library/src/servicelib/exception_utils.py index 2de33fd98e65..0af95d35e4d1 100644 --- a/packages/service-library/src/servicelib/exception_utils.py +++ b/packages/service-library/src/servicelib/exception_utils.py @@ -6,6 +6,7 @@ from typing import Any, Final, ParamSpec, TypeVar from pydantic import BaseModel, Field, NonNegativeFloat, PrivateAttr +from servicelib.logging_errors import create_troubleshootting_log_kwargs _logger = logging.getLogger(__name__) @@ -76,9 +77,65 @@ def else_reset(self) -> None: F = TypeVar("F", bound=Callable[..., Any]) -def silence_exceptions(exceptions: tuple[type[BaseException], ...]) -> Callable[[F], F]: - def _decorator(func_or_coro: F) -> F: +def _should_suppress_exception( + exc: BaseException, + predicate: Callable[[BaseException], bool] | None, + func_name: str, +) -> bool: + if predicate is None: + # No predicate provided, suppress all exceptions + return True + + try: + return predicate(exc) + except Exception as predicate_exc: # pylint: disable=broad-except + # the predicate function raised an exception + # log it and do not suppress the original exception + _logger.warning( + **create_troubleshootting_log_kwargs( + f"Predicate function raised exception {type(predicate_exc).__name__}:{predicate_exc} in {func_name}. " + f"Original exception will be re-raised: {type(exc).__name__}", + error=predicate_exc, + error_context={ + "func_name": func_name, + "original_exception": f"{type(exc).__name__}", + }, + tip="Predicate raised, please fix it.", + ) + ) + return False + + +def suppress_exceptions( + exceptions: tuple[type[BaseException], ...], + *, + reason: str, + predicate: Callable[[BaseException], bool] | None = None, +) -> Callable[[F], F]: + """ + Decorator to suppress specified exceptions. + + Args: + exceptions: Tuple of exception types to suppress + reason: Reason for suppression (for logging) + predicate: Optional function to check exception attributes. + If provided, exception is only suppressed if predicate returns True. + + Example: + # Suppress all ConnectionError exceptions + @suppress_exceptions((ConnectionError,), reason="Network issues") + def my_func(): ... + + # Suppress only ConnectionError with specific errno + @suppress_exceptions( + (ConnectionError,), + reason="Specific network error", + predicate=lambda e: hasattr(e, 'errno') and e.errno == 104 + ) + def my_func(): ... + """ + def _decorator(func_or_coro: F) -> F: if inspect.iscoroutinefunction(func_or_coro): @wraps(func_or_coro) @@ -86,7 +143,19 @@ async def _async_wrapper(*args, **kwargs) -> Any: try: assert inspect.iscoroutinefunction(func_or_coro) # nosec return await func_or_coro(*args, **kwargs) - except exceptions: + except exceptions as exc: + # Check if exception should be suppressed + if not _should_suppress_exception( + exc, predicate, func_or_coro.__name__ + ): + raise # Re-raise if predicate returns False or fails + + _logger.debug( + "Caught suppressed exception %s in %s: TIP: %s", + exc, + func_or_coro.__name__, + reason, + ) return None return _async_wrapper # type: ignore[return-value] # decorators typing is hard @@ -95,7 +164,19 @@ async def _async_wrapper(*args, **kwargs) -> Any: def _sync_wrapper(*args, **kwargs) -> Any: try: return func_or_coro(*args, **kwargs) - except exceptions: + except exceptions as exc: + # Check if exception should be suppressed + if not _should_suppress_exception( + exc, predicate, func_or_coro.__name__ + ): + raise # Re-raise if predicate returns False or fails + + _logger.debug( + "Caught suppressed exception %s in %s: TIP: %s", + exc, + func_or_coro.__name__, + reason, + ) return None return _sync_wrapper # type: ignore[return-value] # decorators typing is hard diff --git a/packages/service-library/src/servicelib/fastapi/http_error.py b/packages/service-library/src/servicelib/fastapi/http_error.py index 2cc9814dc8fa..39838b560585 100644 --- a/packages/service-library/src/servicelib/fastapi/http_error.py +++ b/packages/service-library/src/servicelib/fastapi/http_error.py @@ -11,7 +11,7 @@ from fastapi.responses import JSONResponse from pydantic import ValidationError -from ..logging_errors import create_troubleshotting_log_kwargs +from ..logging_errors import create_troubleshootting_log_kwargs from ..status_codes_utils import is_5xx_server_error validation_error_response_definition["properties"] = { @@ -50,7 +50,7 @@ async def _http_error_handler(request: Request, exc: Exception) -> JSONResponse: if is_5xx_server_error(status_code): _logger.exception( - create_troubleshotting_log_kwargs( + create_troubleshootting_log_kwargs( "Unexpected error happened in the Resource Usage Tracker. Please contact support.", error=exc, error_context={ diff --git a/packages/service-library/src/servicelib/fastapi/long_running_tasks/_context_manager.py b/packages/service-library/src/servicelib/fastapi/long_running_tasks/_context_manager.py index 6a33e4ed5814..3c615c3fc5c6 100644 --- a/packages/service-library/src/servicelib/fastapi/long_running_tasks/_context_manager.py +++ b/packages/service-library/src/servicelib/fastapi/long_running_tasks/_context_manager.py @@ -5,7 +5,7 @@ from typing import Any, Final from pydantic import PositiveFloat -from servicelib.logging_errors import create_troubleshotting_log_message +from servicelib.logging_errors import create_troubleshootting_log_message from ...long_running_tasks.errors import TaskClientTimeoutError, TaskExceptionError from ...long_running_tasks.models import ( @@ -130,7 +130,7 @@ async def _wait_for_task_result() -> Any: except Exception as e: error = TaskExceptionError(task_id=task_id, exception=e, traceback="") _logger.warning( - create_troubleshotting_log_message( + create_troubleshootting_log_message( user_error_msg=f"{task_id=} raised an exception", error=e, tip=f"Check the logs of the service responding to '{client.base_url}'", diff --git a/packages/service-library/src/servicelib/logging_errors.py b/packages/service-library/src/servicelib/logging_errors.py index eaafd2eb73a2..3099aa9b8638 100644 --- a/packages/service-library/src/servicelib/logging_errors.py +++ b/packages/service-library/src/servicelib/logging_errors.py @@ -10,7 +10,7 @@ _logger = logging.getLogger(__name__) -def create_troubleshotting_log_message( +def create_troubleshootting_log_message( user_error_msg: str, *, error: BaseException, @@ -57,7 +57,7 @@ class LogKwargs(TypedDict): extra: LogExtra | None -def create_troubleshotting_log_kwargs( +def create_troubleshootting_log_kwargs( user_error_msg: str, *, error: BaseException, @@ -76,7 +76,11 @@ def create_troubleshotting_log_kwargs( _logger.exception( **create_troubleshotting_log_kwargs( user_error_msg=frontend_msg, - exception=exc, + error=exc, + error_context={ + "user_id": user_id, + "product_name": product_name, + }, tip="Check row in `groups_extra_properties` for this product. It might be missing.", ) ) @@ -88,7 +92,7 @@ def create_troubleshotting_log_kwargs( context.update(error.error_context()) # compose as log message - log_msg = create_troubleshotting_log_message( + log_msg = create_troubleshootting_log_message( user_error_msg, error=error, error_code=error_code, diff --git a/packages/service-library/src/servicelib/long_running_tasks/http_endpoint_responses.py b/packages/service-library/src/servicelib/long_running_tasks/http_endpoint_responses.py index 5256849541b7..50afdfedb8e7 100644 --- a/packages/service-library/src/servicelib/long_running_tasks/http_endpoint_responses.py +++ b/packages/service-library/src/servicelib/long_running_tasks/http_endpoint_responses.py @@ -2,7 +2,7 @@ from typing import Any from common_library.error_codes import create_error_code -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from .errors import TaskNotCompletedError, TaskNotFoundError from .models import TaskBase, TaskId, TaskStatus @@ -43,7 +43,7 @@ async def get_task_result( raise except Exception as exc: _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_error_msg=f"{task_id=} raised an exception", error=exc, error_code=create_error_code(exc), diff --git a/packages/service-library/src/servicelib/redis/_decorators.py b/packages/service-library/src/servicelib/redis/_decorators.py index 6d686a33af59..612b0f815672 100644 --- a/packages/service-library/src/servicelib/redis/_decorators.py +++ b/packages/service-library/src/servicelib/redis/_decorators.py @@ -1,5 +1,4 @@ import asyncio -import contextlib import functools import logging import socket @@ -10,6 +9,7 @@ import arrow import redis.exceptions from redis.asyncio.lock import Lock +from servicelib.logging_errors import create_troubleshootting_log_kwargs from ..background_task import periodic from ._client import RedisClientSDK @@ -23,9 +23,9 @@ R = TypeVar("R") _EXCLUSIVE_TASK_NAME: Final[str] = "exclusive/{module_name}.{func_name}" -_EXCLUSIVE_AUTO_EXTEND_TASK_NAME: Final[ - str -] = "exclusive/autoextend_lock_{redis_lock_key}" +_EXCLUSIVE_AUTO_EXTEND_TASK_NAME: Final[str] = ( + "exclusive/autoextend_lock_{redis_lock_key}" +) @periodic(interval=DEFAULT_LOCK_TTL / 2, raise_on_error=True) @@ -134,10 +134,26 @@ async def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R: assert len(lock_lost_errors.exceptions) == 1 # nosec raise lock_lost_errors.exceptions[0] from eg finally: - with contextlib.suppress(redis.exceptions.LockNotOwnedError): + try: # in the case where the lock would have been lost, # this would raise again and is not necessary await lock.release() + except redis.exceptions.LockNotOwnedError as exc: + _logger.exception( + **create_troubleshootting_log_kwargs( + f"Unexpected error while releasing lock '{redis_lock_key}'", + error=exc, + error_context={ + "redis_lock_key": redis_lock_key, + "lock_value": lock_value, + "client_name": client.client_name, + "hostname": socket.gethostname(), + "coroutine": coro.__name__, + }, + tip="This might happen if the lock was lost before releasing it. " + "Look for synchronous code that prevents refreshing the lock or asyncio loop overload.", + ) + ) return _wrapper diff --git a/packages/service-library/tests/test_exception_utils.py b/packages/service-library/tests/test_exception_utils.py index a884d3dafb19..040022b64eb6 100644 --- a/packages/service-library/tests/test_exception_utils.py +++ b/packages/service-library/tests/test_exception_utils.py @@ -4,7 +4,7 @@ import pytest from pydantic import PositiveFloat, PositiveInt -from servicelib.exception_utils import DelayedExceptionHandler, silence_exceptions +from servicelib.exception_utils import DelayedExceptionHandler, suppress_exceptions TOLERANCE: Final[PositiveFloat] = 0.1 SLEEP_FOR: Final[PositiveFloat] = TOLERANCE * 0.1 @@ -53,14 +53,17 @@ def test_workflow_raises() -> None: # Define some custom exceptions for testing class CustomError(Exception): - pass + def __init__(self, code: int = 0, message: str = ""): + self.code = code + self.message = message + super().__init__(message) class AnotherCustomError(Exception): pass -@silence_exceptions((CustomError,)) +@suppress_exceptions((CustomError,), reason="CustomError is silenced") def sync_function(*, raise_error: bool, raise_another_error: bool) -> str: if raise_error: raise CustomError @@ -69,7 +72,7 @@ def sync_function(*, raise_error: bool, raise_another_error: bool) -> str: return "Success" -@silence_exceptions((CustomError,)) +@suppress_exceptions((CustomError,), reason="CustomError is silenced") async def async_function(*, raise_error: bool, raise_another_error: bool) -> str: if raise_error: raise CustomError @@ -78,6 +81,29 @@ async def async_function(*, raise_error: bool, raise_another_error: bool) -> str return "Success" +# Test functions with predicate +@suppress_exceptions( + (CustomError,), + reason="Only suppress CustomError with code >= 100", + predicate=lambda e: hasattr(e, "code") and e.code >= 100, +) +def sync_function_with_predicate(error_code: int = 0) -> str: + if error_code > 0: + raise CustomError(code=error_code, message=f"Error {error_code}") + return "Success" + + +@suppress_exceptions( + (CustomError,), + reason="Only suppress CustomError with code >= 100", + predicate=lambda e: hasattr(e, "code") and e.code >= 100, +) +async def async_function_with_predicate(error_code: int = 0) -> str: + if error_code > 0: + raise CustomError(code=error_code, message=f"Error {error_code}") + return "Success" + + def test_sync_function_no_exception(): result = sync_function(raise_error=False, raise_another_error=False) assert result == "Success" @@ -106,3 +132,144 @@ def test_sync_function_with_different_exception(): async def test_async_function_with_different_exception(): with pytest.raises(AnotherCustomError): await async_function(raise_error=False, raise_another_error=True) + + +def test_sync_function_predicate_suppresses_matching_exception(): + result = sync_function_with_predicate( + error_code=150 + ) # code >= 100, should be suppressed + assert result is None + + +def test_sync_function_predicate_raises_non_matching_exception(): + with pytest.raises(CustomError): + sync_function_with_predicate(error_code=50) # code < 100, should be raised + + +def test_sync_function_predicate_no_exception(): + result = sync_function_with_predicate(error_code=0) + assert result == "Success" + + +async def test_async_function_predicate_suppresses_matching_exception(): + result = await async_function_with_predicate( + error_code=200 + ) # code >= 100, should be suppressed + assert result is None + + +async def test_async_function_predicate_raises_non_matching_exception(): + with pytest.raises(CustomError): + await async_function_with_predicate( + error_code=25 + ) # code < 100, should be raised + + +async def test_async_function_predicate_no_exception(): + result = await async_function_with_predicate(error_code=0) + assert result == "Success" + + +@suppress_exceptions( + (ValueError, TypeError), + reason="Complex predicate test", + predicate=lambda e: "suppress" in str(e).lower(), +) +def function_with_complex_predicate(message: str) -> str: + if "value" in message: + raise ValueError(message) + if "type" in message: + raise TypeError(message) + return "Success" + + +def test_complex_predicate_suppresses_matching(): + result = function_with_complex_predicate("please suppress this value error") + assert result is None + + +def test_complex_predicate_raises_non_matching(): + with pytest.raises(ValueError, match="value error without keyword"): + function_with_complex_predicate("value error without keyword") + + +def test_complex_predicate_different_exception_type(): + result = function_with_complex_predicate("type error with suppress keyword") + assert result is None + + +# Test predicate exception handling +@suppress_exceptions( + (ValueError,), + reason="Predicate that raises exception", + predicate=lambda _: bool(1 / 0), # This will raise ZeroDivisionError +) +def function_with_failing_predicate() -> str: + msg = "Original error" + raise ValueError(msg) + + +@suppress_exceptions( + (ValueError,), + reason="Predicate that raises exception", + predicate=lambda _: bool(1 / 0), # This will raise ZeroDivisionError +) +async def async_function_with_failing_predicate() -> str: + msg = "Original error" + raise ValueError(msg) + + +def test_sync_function_predicate_exception_reraised(caplog): + with pytest.raises(ValueError, match="Original error"): + function_with_failing_predicate() + + # Check that warning was logged + assert "Predicate function raised exception" in caplog.text + assert "ZeroDivisionError" in caplog.text + + +async def test_async_function_predicate_exception_reraised(caplog): + with pytest.raises(ValueError, match="Original error"): + await async_function_with_failing_predicate() + + # Check that warning was logged + assert "Predicate function raised exception" in caplog.text + assert "ZeroDivisionError" in caplog.text + + +@suppress_exceptions( + (ValueError,), + reason="Predicate that accesses invalid attribute", + predicate=lambda e: e.nonexistent_attribute == "test", +) +def function_with_attribute_error_predicate() -> str: + msg = "Original error" + raise ValueError(msg) + + +def test_predicate_attribute_error_reraised(caplog): + with pytest.raises(ValueError, match="Original error"): + function_with_attribute_error_predicate() + + # Check that warning was logged about predicate failure + assert "Predicate function raised exception" in caplog.text + assert "AttributeError" in caplog.text + + +@suppress_exceptions( + (ValueError,), + reason="Predicate that sometimes works", + predicate=lambda e: len(str(e)) > 5, # Safe predicate +) +def function_with_working_predicate(message: str) -> str: + raise ValueError(message) + + +def test_predicate_works_normally(): + # Short message - predicate returns False, exception re-raised + with pytest.raises(ValueError): + function_with_working_predicate("Hi") + + # Long message - predicate returns True, exception suppressed + result = function_with_working_predicate("This is a long error message") + assert result is None diff --git a/packages/service-library/tests/test_logging_errors.py b/packages/service-library/tests/test_logging_errors.py index 5baf6166943b..ca9b669f0edc 100644 --- a/packages/service-library/tests/test_logging_errors.py +++ b/packages/service-library/tests/test_logging_errors.py @@ -6,8 +6,8 @@ from common_library.error_codes import create_error_code, parse_error_code_parts from common_library.errors_classes import OsparcErrorMixin from servicelib.logging_errors import ( - create_troubleshotting_log_kwargs, - create_troubleshotting_log_message, + create_troubleshootting_log_kwargs, + create_troubleshootting_log_message, ) @@ -29,7 +29,7 @@ class MyError(OsparcErrorMixin, RuntimeError): msg = f"Nice message to user [{error_code}]" - log_msg = create_troubleshotting_log_message( + log_msg = create_troubleshootting_log_message( msg, error=exc, error_code=error_code, @@ -37,7 +37,7 @@ class MyError(OsparcErrorMixin, RuntimeError): tip="This is a test error", ) - log_kwargs = create_troubleshotting_log_kwargs( + log_kwargs = create_troubleshootting_log_kwargs( msg, error=exc, error_code=error_code, diff --git a/services/api-server/src/simcore_service_api_server/exceptions/handlers/_handlers_factory.py b/services/api-server/src/simcore_service_api_server/exceptions/handlers/_handlers_factory.py index fe2befdce63c..0e081557145a 100644 --- a/services/api-server/src/simcore_service_api_server/exceptions/handlers/_handlers_factory.py +++ b/services/api-server/src/simcore_service_api_server/exceptions/handlers/_handlers_factory.py @@ -3,7 +3,7 @@ from common_library.error_codes import create_error_code from fastapi.requests import Request from fastapi.responses import JSONResponse -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from ._utils import ExceptionHandler, create_error_json_response @@ -40,7 +40,7 @@ async def _http_error_handler( user_error_msg += f" [{error_code}]" _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_error_msg, error=exception, error_code=error_code, diff --git a/services/api-server/src/simcore_service_api_server/services_http/log_streaming.py b/services/api-server/src/simcore_service_api_server/services_http/log_streaming.py index 1fcf65bdf423..5781659d1c74 100644 --- a/services/api-server/src/simcore_service_api_server/services_http/log_streaming.py +++ b/services/api-server/src/simcore_service_api_server/services_http/log_streaming.py @@ -8,7 +8,7 @@ from models_library.rabbitmq_messages import LoggerRabbitMessage from models_library.users import UserID from pydantic import NonNegativeInt -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from servicelib.logging_utils import log_catch from servicelib.rabbitmq import QueueName, RabbitMQClient @@ -139,7 +139,7 @@ async def log_generator(self) -> AsyncIterable[str]: error_msg = MSG_INTERNAL_ERROR_USER_FRIENDLY_TEMPLATE + f" [{error_code}]" _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( error_msg, error=exc, error_code=error_code, diff --git a/services/catalog/src/simcore_service_catalog/service/catalog_services.py b/services/catalog/src/simcore_service_catalog/service/catalog_services.py index d1377bb4db6c..95ced884a26a 100644 --- a/services/catalog/src/simcore_service_catalog/service/catalog_services.py +++ b/services/catalog/src/simcore_service_catalog/service/catalog_services.py @@ -23,7 +23,7 @@ from models_library.users import UserID from pydantic import HttpUrl from servicelib.logging_errors import ( - create_troubleshotting_log_kwargs, + create_troubleshootting_log_kwargs, ) from servicelib.rabbitmq.rpc_interfaces.catalog.errors import ( CatalogForbiddenError, @@ -239,7 +239,7 @@ async def _get_services_manifests( if missing_services: msg = f"Found {len(missing_services)} services that are in the database but missing in the registry manifest" _logger.warning( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( msg, error=CatalogInconsistentError( missing_services=missing_services, diff --git a/services/director-v2/src/simcore_service_director_v2/modules/comp_scheduler/_manager.py b/services/director-v2/src/simcore_service_director_v2/modules/comp_scheduler/_manager.py index dd5ebbf20224..09478d4d02e5 100644 --- a/services/director-v2/src/simcore_service_director_v2/modules/comp_scheduler/_manager.py +++ b/services/director-v2/src/simcore_service_director_v2/modules/comp_scheduler/_manager.py @@ -7,7 +7,7 @@ from models_library.users import UserID from servicelib.async_utils import cancel_wait_task from servicelib.background_task import create_periodic_task -from servicelib.exception_utils import silence_exceptions +from servicelib.exception_utils import suppress_exceptions from servicelib.logging_utils import log_context from servicelib.redis import CouldNotAcquireLockError, exclusive from servicelib.utils import limited_gather @@ -191,7 +191,10 @@ async def schedule_all_pipelines(app: FastAPI) -> None: async def setup_manager(app: FastAPI) -> None: app.state.scheduler_manager = create_periodic_task( - silence_exceptions((CouldNotAcquireLockError,))(schedule_all_pipelines), + suppress_exceptions( + (CouldNotAcquireLockError,), + reason="Multiple instances of the periodic task `computational scheduler manager` are running.", + )(schedule_all_pipelines), interval=SCHEDULER_INTERVAL, task_name=MODULE_NAME_SCHEDULER, app=app, diff --git a/services/director-v2/src/simcore_service_director_v2/modules/dynamic_sidecar/scheduler/_core/_observer.py b/services/director-v2/src/simcore_service_director_v2/modules/dynamic_sidecar/scheduler/_core/_observer.py index 949ba98f4fe4..3f96413f48ef 100644 --- a/services/director-v2/src/simcore_service_director_v2/modules/dynamic_sidecar/scheduler/_core/_observer.py +++ b/services/director-v2/src/simcore_service_director_v2/modules/dynamic_sidecar/scheduler/_core/_observer.py @@ -7,7 +7,7 @@ from common_library.error_codes import create_error_code from fastapi import FastAPI -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from .....core.dynamic_services_settings.scheduler import ( DynamicServicesSchedulerSettings, @@ -153,7 +153,7 @@ async def observing_single_service( error_code = create_error_code(exc) logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_error_msg, error=exc, error_context={ diff --git a/services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/core/utils.py b/services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/core/utils.py index 3993ce37a56d..8dba8270e11d 100644 --- a/services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/core/utils.py +++ b/services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/core/utils.py @@ -8,7 +8,7 @@ import psutil from common_library.error_codes import create_error_code -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from ..modules.mounted_fs import MountedVolumes @@ -110,7 +110,7 @@ async def async_command( error_code = create_error_code(err) user_error_msg = f"Unexpected error [{error_code}]" _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_error_msg, error=err, error_context={"command": command, "proc.returncode": proc.returncode}, diff --git a/services/invitations/src/simcore_service_invitations/core/exceptions_handlers.py b/services/invitations/src/simcore_service_invitations/core/exceptions_handlers.py index 47c72be56a84..c3c89f67d38e 100644 --- a/services/invitations/src/simcore_service_invitations/core/exceptions_handlers.py +++ b/services/invitations/src/simcore_service_invitations/core/exceptions_handlers.py @@ -2,7 +2,7 @@ from fastapi import FastAPI, Request, status from fastapi.responses import JSONResponse -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from ..services.invitations import InvalidInvitationCodeError @@ -16,7 +16,7 @@ def handle_invalid_invitation_code_error(request: Request, exception: Exception) user_msg = INVALID_INVITATION_URL_MSG _logger.warning( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_msg, error=exception, error_context={ diff --git a/services/payments/src/simcore_service_payments/api/rest/_acknowledgements.py b/services/payments/src/simcore_service_payments/api/rest/_acknowledgements.py index ca0d74c8e3e7..9a46d2469b6c 100644 --- a/services/payments/src/simcore_service_payments/api/rest/_acknowledgements.py +++ b/services/payments/src/simcore_service_payments/api/rest/_acknowledgements.py @@ -6,7 +6,7 @@ PaymentMethodNotFoundError, PaymentNotFoundError, ) -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from servicelib.logging_utils import log_context from ..._constants import ACKED, PGDB @@ -80,7 +80,7 @@ async def acknowledge_payment( if ack.saved: if ack.saved.payment_method_id is None or not ack.saved.success: _logger.error( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( f"Got ack that {payment_id=} was completed but failed to save the payment-method used for the payment as requested.", error=RuntimeError("Failed to save payment-method after payment"), error_context={ diff --git a/services/payments/src/simcore_service_payments/services/notifier_email.py b/services/payments/src/simcore_service_payments/services/notifier_email.py index 29a423837df8..422ab9efb350 100644 --- a/services/payments/src/simcore_service_payments/services/notifier_email.py +++ b/services/payments/src/simcore_service_payments/services/notifier_email.py @@ -15,7 +15,7 @@ from models_library.products import ProductName from models_library.users import UserID from pydantic import EmailStr -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from settings_library.email import EmailProtocol, SMTPSettings from tenacity import ( retry, @@ -239,7 +239,7 @@ async def _create_user_email( except Exception as exc: # pylint: disable=broad-exception-caught _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( "Cannot attach invoice to payment. Email sent w/o attached pdf invoice", error=exc, error_context={ diff --git a/services/resource-usage-tracker/src/simcore_service_resource_usage_tracker/exceptions/handlers/_http_error.py b/services/resource-usage-tracker/src/simcore_service_resource_usage_tracker/exceptions/handlers/_http_error.py index 3ab692a70dce..05ba5d7a3dcb 100644 --- a/services/resource-usage-tracker/src/simcore_service_resource_usage_tracker/exceptions/handlers/_http_error.py +++ b/services/resource-usage-tracker/src/simcore_service_resource_usage_tracker/exceptions/handlers/_http_error.py @@ -1,10 +1,9 @@ import logging -from collections.abc import Callable -from typing import Awaitable +from collections.abc import Awaitable, Callable from fastapi import HTTPException, status from fastapi.encoders import jsonable_encoder -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from servicelib.status_codes_utils import is_5xx_server_error from starlette.requests import Request from starlette.responses import JSONResponse @@ -19,7 +18,7 @@ async def http_error_handler(request: Request, exc: Exception) -> JSONResponse: if is_5xx_server_error(exc.status_code): _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( "Unexpected error happened in the Resource Usage Tracker. Please contact support.", error=exc, error_context={ diff --git a/services/web/server/src/simcore_service_webserver/catalog/_controller_rest_exceptions.py b/services/web/server/src/simcore_service_webserver/catalog/_controller_rest_exceptions.py index 244cb5a34a07..3f685f644a00 100644 --- a/services/web/server/src/simcore_service_webserver/catalog/_controller_rest_exceptions.py +++ b/services/web/server/src/simcore_service_webserver/catalog/_controller_rest_exceptions.py @@ -7,7 +7,7 @@ from common_library.user_messages import user_message from models_library.rest_error import ErrorGet from servicelib.aiohttp import status -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from servicelib.rabbitmq._errors import RemoteMethodNotRegisteredError from servicelib.rabbitmq.rpc_interfaces.catalog.errors import ( CatalogForbiddenError, @@ -64,7 +64,7 @@ async def _handler_catalog_client_errors( # Log for further investigation oec = create_error_code(exception) _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_msg, error=exception, error_code=oec, diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_controller/_rest_exceptions.py b/services/web/server/src/simcore_service_webserver/director_v2/_controller/_rest_exceptions.py index 2d866c48b387..ac1958870a44 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_controller/_rest_exceptions.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_controller/_rest_exceptions.py @@ -6,7 +6,7 @@ from models_library.rest_error import ErrorGet from servicelib import status_codes_utils from servicelib.aiohttp import status -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from ...constants import MSG_TRY_AGAIN_OR_SUPPORT from ...exception_handling import ( @@ -54,7 +54,7 @@ async def _handler_director_service_error_as_503_or_4xx( # Log for further investigation oec = create_error_code(exception) _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_msg, error=exception, error_code=oec, diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_director_v2_service.py b/services/web/server/src/simcore_service_webserver/director_v2/_director_v2_service.py index 0670781c281c..2bbc971f8879 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_director_v2_service.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_director_v2_service.py @@ -15,7 +15,8 @@ from pydantic import TypeAdapter from pydantic.types import PositiveInt from servicelib.aiohttp import status -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.exception_utils import suppress_exceptions +from servicelib.logging_errors import create_troubleshootting_log_kwargs from servicelib.logging_utils import log_decorator from simcore_postgres_database.utils_groups_extra_properties import ( GroupExtraProperties, @@ -79,7 +80,7 @@ async def create_or_update_pipeline( except DirectorV2ServiceError as exc: _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( f"Could not create pipeline from project {project_id}", error=exc, error_context={**body, "backend_url": backend_url}, @@ -112,7 +113,6 @@ async def is_pipeline_running( async def get_computation_task( app: web.Application, user_id: UserID, project_id: ProjectID ) -> ComputationTask | None: - try: dv2_computation = await DirectorV2RestClient(app).get_computation( project_id=project_id, user_id=user_id @@ -131,7 +131,17 @@ async def get_computation_task( return None +def _skip_if_pipeline_not_found(exception: BaseException) -> bool: + assert isinstance(exception, DirectorV2ServiceError) # nosec + return exception.status == status.HTTP_404_NOT_FOUND + + @log_decorator(logger=_logger) +@suppress_exceptions( + (DirectorV2ServiceError,), + reason="silence in case the pipeline does not exist", + predicate=_skip_if_pipeline_not_found, +) async def stop_pipeline( app: web.Application, *, user_id: PositiveInt, project_id: ProjectID ): diff --git a/services/web/server/src/simcore_service_webserver/exception_handling/_factory.py b/services/web/server/src/simcore_service_webserver/exception_handling/_factory.py index b8e1bb4276e2..ffa74dc59175 100644 --- a/services/web/server/src/simcore_service_webserver/exception_handling/_factory.py +++ b/services/web/server/src/simcore_service_webserver/exception_handling/_factory.py @@ -7,7 +7,7 @@ from models_library.rest_error import ErrorGet from servicelib.aiohttp.rest_responses import safe_status_message from servicelib.aiohttp.web_exceptions_extension import get_all_aiohttp_http_exceptions -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from servicelib.status_codes_utils import is_5xx_server_error, is_error from ._base import AiohttpExceptionHandler, ExceptionHandlersMap @@ -102,7 +102,7 @@ async def _exception_handler( } _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_msg, error=exception, error_code=oec, diff --git a/services/web/server/src/simcore_service_webserver/garbage_collector/_core_orphans.py b/services/web/server/src/simcore_service_webserver/garbage_collector/_core_orphans.py index 1020f1dcb1d5..2d23be47b826 100644 --- a/services/web/server/src/simcore_service_webserver/garbage_collector/_core_orphans.py +++ b/services/web/server/src/simcore_service_webserver/garbage_collector/_core_orphans.py @@ -10,7 +10,7 @@ from models_library.projects import ProjectID from models_library.projects_nodes_io import NodeID from servicelib.common_headers import UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from servicelib.logging_utils import log_catch, log_context from servicelib.utils import limited_as_completed, limited_gather @@ -119,7 +119,7 @@ async def remove_orphaned_services( potentially_running_service_ids.append(project_nodes) except BaseException as e: # pylint:disable=broad-exception-caught _logger.warning( - create_troubleshotting_log_kwargs( + create_troubleshootting_log_kwargs( ( "Skipping orpahn services removal, call to " "`list_node_ids_in_project` raised" diff --git a/services/web/server/src/simcore_service_webserver/groups/_classifiers_service.py b/services/web/server/src/simcore_service_webserver/groups/_classifiers_service.py index 5adf4ce3e380..2c06edb571fe 100644 --- a/services/web/server/src/simcore_service_webserver/groups/_classifiers_service.py +++ b/services/web/server/src/simcore_service_webserver/groups/_classifiers_service.py @@ -23,7 +23,7 @@ ValidationError, field_validator, ) -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from simcore_postgres_database.models.classifiers import group_classifiers from ..db.plugin import get_database_engine @@ -99,7 +99,7 @@ async def get_classifiers_from_bundle(self, gid: int) -> dict[str, Any]: ) except ValidationError as err: _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( f"DB corrupt data in 'groups_classifiers' table. Invalid classifier for gid={gid}. Returning empty bundle.", error=err, error_context={ diff --git a/services/web/server/src/simcore_service_webserver/login/_controller/rest/change.py b/services/web/server/src/simcore_service_webserver/login/_controller/rest/change.py index 5874b4e80edc..7f695e44ed7f 100644 --- a/services/web/server/src/simcore_service_webserver/login/_controller/rest/change.py +++ b/services/web/server/src/simcore_service_webserver/login/_controller/rest/change.py @@ -5,7 +5,7 @@ from models_library.emails import LowerCaseEmailStr from pydantic import SecretStr, field_validator from servicelib.aiohttp.requests_validation import parse_request_body_as -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from servicelib.request_keys import RQT_USERID_KEY from simcore_postgres_database.utils_repos import pass_or_acquire_connection @@ -130,7 +130,7 @@ def _get_error_context( user = await db.get_user({"email": request_body.email}) if not user: _logger.warning( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( f"{_error_msg_prefix} for non-existent email. {_error_msg_suffix}", error=Exception("No user found with this email"), error_context=_get_error_context(), @@ -149,7 +149,7 @@ def _get_error_context( # NOTE: we abuse here (untiby reusing `validate_user_status` and catching http errors that we # do not want to forward but rather log due to the special rules in this entrypoint _logger.warning( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( f"{_error_msg_prefix} for invalid user. {err.text}. {_error_msg_suffix}", error=err, error_context={**_get_error_context(user), "error.text": err.text}, @@ -167,7 +167,7 @@ def _get_error_context( request.app, user_id=user["id"], product_name=product.name ): _logger.warning( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( f"{_error_msg_prefix} for a user with NO access to this product. {_error_msg_suffix}", error=Exception("User cannot access this product"), error_context=_get_error_context(user), @@ -210,7 +210,7 @@ def _get_error_context( ) except Exception as err: # pylint: disable=broad-except _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( "Unable to send email", error=err, error_context=_get_error_context(user), diff --git a/services/web/server/src/simcore_service_webserver/login/_controller/rest/confirmation.py b/services/web/server/src/simcore_service_webserver/login/_controller/rest/confirmation.py index 04ad8307e3f1..d263081a9a01 100644 --- a/services/web/server/src/simcore_service_webserver/login/_controller/rest/confirmation.py +++ b/services/web/server/src/simcore_service_webserver/login/_controller/rest/confirmation.py @@ -21,7 +21,7 @@ parse_request_body_as, parse_request_path_parameters_as, ) -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from simcore_postgres_database.aiopg_errors import UniqueViolation from yarl import URL @@ -195,7 +195,7 @@ async def validate_confirmation_and_redirect(request: web.Request): ) _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_error_msg, error=err, error_code=error_code, diff --git a/services/web/server/src/simcore_service_webserver/login/_controller/rest/registration.py b/services/web/server/src/simcore_service_webserver/login/_controller/rest/registration.py index 96eeda1f8ff4..920edfc1ea35 100644 --- a/services/web/server/src/simcore_service_webserver/login/_controller/rest/registration.py +++ b/services/web/server/src/simcore_service_webserver/login/_controller/rest/registration.py @@ -16,7 +16,7 @@ ) from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import parse_request_body_as -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from simcore_postgres_database.models.users import UserStatus @@ -284,7 +284,7 @@ async def register(request: web.Request): user_error_msg = MSG_CANT_SEND_MAIL _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_error_msg, error=err, error_code=error_code, @@ -426,7 +426,7 @@ async def register_phone(request: web.Request): user_error_msg = "Currently we cannot register phone numbers" _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_error_msg, error=err, error_code=error_code, diff --git a/services/web/server/src/simcore_service_webserver/login/_invitations_service.py b/services/web/server/src/simcore_service_webserver/login/_invitations_service.py index 7b0b5de660aa..a7cd5d620401 100644 --- a/services/web/server/src/simcore_service_webserver/login/_invitations_service.py +++ b/services/web/server/src/simcore_service_webserver/login/_invitations_service.py @@ -23,7 +23,7 @@ ValidationError, field_validator, ) -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from simcore_postgres_database.models.confirmations import ConfirmationAction from simcore_postgres_database.models.users import UserStatus @@ -222,7 +222,7 @@ def _invitations_request_context(invitation_code: str) -> Iterator[URL]: user_error_msg = f"Invalid invitation. {MSG_INVITATIONS_CONTACT_SUFFIX}" _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_error_msg, error=err, error_code=error_code, @@ -239,7 +239,7 @@ def _invitations_request_context(invitation_code: str) -> Iterator[URL]: user_error_msg = "Unable to process your invitation since the invitations service is currently unavailable" _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_error_msg, error=err, error_code=error_code, diff --git a/services/web/server/src/simcore_service_webserver/login/errors.py b/services/web/server/src/simcore_service_webserver/login/errors.py index 628014621b9d..dc6d9ec54cf1 100644 --- a/services/web/server/src/simcore_service_webserver/login/errors.py +++ b/services/web/server/src/simcore_service_webserver/login/errors.py @@ -3,7 +3,7 @@ from aiohttp import web from servicelib.aiohttp.typing_extension import Handler -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from ..errors import WebServerBaseError @@ -34,7 +34,7 @@ async def wrapper(request: web.Request) -> web.StreamResponse: front_end_msg = MSG_2FA_UNAVAILABLE # in these cases I want to log the cause _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( front_end_msg, error=exc, error_code=error_code, diff --git a/services/web/server/src/simcore_service_webserver/security/_authz_policy.py b/services/web/server/src/simcore_service_webserver/security/_authz_policy.py index 1c64389279f2..0df137dece45 100644 --- a/services/web/server/src/simcore_service_webserver/security/_authz_policy.py +++ b/services/web/server/src/simcore_service_webserver/security/_authz_policy.py @@ -13,7 +13,7 @@ from models_library.products import ProductName from models_library.users import UserID from servicelib.aiohttp.db_asyncpg_engine import get_async_engine -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from simcore_postgres_database.aiopg_errors import DatabaseError as AiopgDatabaseError from sqlalchemy.exc import DatabaseError as SQLAlchemyDatabaseError @@ -51,7 +51,7 @@ def _handle_exceptions_as_503(): yield except (AiopgDatabaseError, SQLAlchemyDatabaseError) as err: _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( "Auth unavailable due to database error", error=err, tip="Check database connection", diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py index dbe821145184..168f60d69f75 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py @@ -19,7 +19,7 @@ from models_library.projects_nodes_io import DownloadLink, NodeID, PortLink from models_library.services import ServiceKey, ServiceVersion from pydantic import AnyUrl, HttpUrl, TypeAdapter -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from servicelib.logging_utils import log_decorator from ..projects._projects_repository_legacy import ProjectDBAPI @@ -284,7 +284,7 @@ async def get_or_create_project_with_file_and_service( else: http_500_error = web.HTTPInternalServerError() _logger.error( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( f"Project {project_uid} exists but does not seem to be a viewer generated by this module.", error=http_500_error, error_context={ diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_redirects_handlers.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_redirects_handlers.py index d4f53399ccbd..e51303599154 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_redirects_handlers.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_redirects_handlers.py @@ -14,7 +14,7 @@ from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import parse_request_query_parameters_as from servicelib.aiohttp.typing_extension import Handler -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from ..dynamic_scheduler import api as dynamic_scheduler_service from ..products import products_web @@ -128,7 +128,7 @@ async def wrapper(request: web.Request) -> web.StreamResponse: msg=MSG_UNEXPECTED_DISPATCH_ERROR, error_code=error_code ) _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_error_msg, error=err, error_code=error_code, diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_studies_access.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_studies_access.py index ae52d91b8cc1..0deaa0496ef2 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_studies_access.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_studies_access.py @@ -24,7 +24,7 @@ from models_library.projects import ProjectID from servicelib.aiohttp import status from servicelib.aiohttp.typing_extension import Handler -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from ..constants import INDEX_RESOURCE_NAME from ..director_v2 import director_v2_service @@ -268,7 +268,7 @@ async def wrapper(request: web.Request) -> web.StreamResponse: msg=MSG_UNEXPECTED_DISPATCH_ERROR, error_code=error_code ) _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_error_msg, error=err, error_code=error_code, @@ -341,7 +341,7 @@ async def get_redirection_to_study_page(request: web.Request) -> web.Response: user_error_msg = MSG_TOO_MANY_GUESTS _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_error_msg, error=exc, error_code=error_code, @@ -373,7 +373,7 @@ async def get_redirection_to_study_page(request: web.Request) -> web.Response: user_error_msg = MSG_UNEXPECTED_DISPATCH_ERROR _logger.exception( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( user_error_msg, error=exc, error_code=error_code, diff --git a/services/web/server/src/simcore_service_webserver/trash/_service.py b/services/web/server/src/simcore_service_webserver/trash/_service.py index 502d4d2c1373..968cd458dd10 100644 --- a/services/web/server/src/simcore_service_webserver/trash/_service.py +++ b/services/web/server/src/simcore_service_webserver/trash/_service.py @@ -7,7 +7,7 @@ from aiohttp import web from models_library.products import ProductName from models_library.users import UserID -from servicelib.logging_errors import create_troubleshotting_log_kwargs +from servicelib.logging_errors import create_troubleshootting_log_kwargs from servicelib.logging_utils import log_context from ..folders import folders_trash_service @@ -42,7 +42,6 @@ async def _empty_explicitly_trashed_projects( ): for project_id in trashed_projects_ids: try: - await projects_trash_service.delete_explicitly_trashed_project( app, user_id=user_id, @@ -51,7 +50,7 @@ async def _empty_explicitly_trashed_projects( except Exception as exc: # pylint: disable=broad-exception-caught _logger.warning( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( "Error deleting a trashed project while emptying trash.", error=exc, error_context={ @@ -88,7 +87,7 @@ async def _empty_explicitly_trashed_folders_and_content( except Exception as exc: # pylint: disable=broad-exception-caught _logger.warning( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( "Error deleting a trashed folders (and content) while emptying trash.", error=exc, error_context={ @@ -125,7 +124,7 @@ async def _empty_explicitely_trashed_workspaces_and_content( except Exception as exc: # pylint: disable=broad-exception-caught _logger.warning( - **create_troubleshotting_log_kwargs( + **create_troubleshootting_log_kwargs( "Error deleting a trashed workspace (and content) while emptying trash.", error=exc, error_context={ @@ -143,7 +142,7 @@ async def safe_empty_trash( *, product_name: ProductName, user_id: UserID, - on_explicitly_trashed_projects_deleted: asyncio.Event | None = None + on_explicitly_trashed_projects_deleted: asyncio.Event | None = None, ): # Delete explicitly trashed projects & notify await _empty_explicitly_trashed_projects(app, product_name, user_id) @@ -171,7 +170,6 @@ async def safe_delete_expired_trash_as_admin(app: web.Application) -> None: retention, delete_until, ): - ctx = { "delete_until": delete_until, "retention": retention, @@ -188,9 +186,9 @@ async def safe_delete_expired_trash_as_admin(app: web.Application) -> None: _logger.info("Deleted %d trashed workspaces", len(deleted_workspace_ids)) except Exception as exc: # pylint: disable=broad-exception-caught - _logger.warning( - **create_troubleshotting_log_kwargs( - "Error batch deleting expired workspaces as admin.", + _logger.exception( + **create_troubleshootting_log_kwargs( + "Unexpected error while batch deleting expired workspaces as admin:", error=exc, error_context=ctx, ) @@ -207,9 +205,9 @@ async def safe_delete_expired_trash_as_admin(app: web.Application) -> None: except Exception as exc: # pylint: disable=broad-exception-caught ctx_with_product = {**ctx, "product_name": product_name} - _logger.warning( - **create_troubleshotting_log_kwargs( - "Error batch deleting expired trashed folders as admin.", + _logger.exception( + **create_troubleshootting_log_kwargs( + "Unexpected error while batch deleting expired trashed folders as admin:", error=exc, error_context=ctx_with_product, ) @@ -227,9 +225,9 @@ async def safe_delete_expired_trash_as_admin(app: web.Application) -> None: _logger.info("Deleted %d trashed projects", len(deleted_project_ids)) except Exception as exc: # pylint: disable=broad-exception-caught - _logger.warning( - **create_troubleshotting_log_kwargs( - "Error batch deleting expired projects as admin.", + _logger.exception( + **create_troubleshootting_log_kwargs( + "Unexpected error while batch deleting expired projects as admin:", error=exc, error_context=ctx, )