From 27953e9568086a883330d43a00db287de50e883e Mon Sep 17 00:00:00 2001 From: Gustavo-Hagenbeck Date: Mon, 8 Dec 2025 10:40:15 -0300 Subject: [PATCH] Replace traceback.print_exc() with logger.exception() for proper logging Using traceback.print_exc() and traceback.print_exception() writes directly to stderr, bypassing the Python logging system. This prevents users from: - Configuring log destinations (files, syslog, cloud services) - Controlling log levels and filtering - Using structured logging formats (JSON, etc.) - Integrating with monitoring and alerting systems PEP 337 explicitly recommends: "Replace traceback.print_exception with _log.exception" and states that modules should "use the logging system instead of print or sys.stdout.write." Changes: - Added logging.getLogger(__name__) to server.py and server_context.py - Replaced traceback.print_exc() with logger.exception() in 2 locations - Replaced traceback.print_exception() with logger.exception() in 2 locations This allows applications using the SDK to properly control exception logging through standard Python logging configuration. References: - PEP 337: https://peps.python.org/pep-0337/ - Python Logging HOWTO: https://docs.python.org/3/howto/logging.html --- python/restate/server.py | 8 +++++--- python/restate/server_context.py | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/python/restate/server.py b/python/restate/server.py index fc6dd8d..587d820 100644 --- a/python/restate/server.py +++ b/python/restate/server.py @@ -11,8 +11,8 @@ """This module contains the ASGI server for the restate framework.""" import asyncio +import logging from typing import Dict, TypedDict, Literal -import traceback from restate.discovery import compute_discovery_json from restate.endpoint import Endpoint @@ -24,6 +24,8 @@ from restate._internal import PyIdentityVerifier, IdentityVerificationException # pylint: disable=import-error,no-name-in-module from restate._internal import SDK_VERSION # pylint: disable=import-error,no-name-in-module +logger = logging.getLogger(__name__) + X_RESTATE_SERVER = header_to_binary([("x-restate-server", f"restate-sdk-python/{SDK_VERSION}")]) @@ -165,7 +167,7 @@ async def process_invocation_to_completion( return # pylint: disable=W0718 except Exception: - traceback.print_exc() + logger.exception("Exception in Restate handler") try: await context.leave() finally: @@ -272,7 +274,7 @@ async def app(scope: Scope, receive: Receive, send: Send): except LifeSpanNotImplemented as e: raise e except Exception as e: - traceback.print_exc() + logger.exception("Exception in ASGI app") raise e if is_running_on_lambda(): diff --git a/python/restate/server_context.py b/python/restate/server_context.py index efa4e69..31ae4be 100644 --- a/python/restate/server_context.py +++ b/python/restate/server_context.py @@ -24,6 +24,7 @@ from datetime import timedelta import inspect import functools +import logging from typing import Any, Awaitable, Callable, Dict, List, Optional, TypeVar, Union, Coroutine import typing import traceback @@ -59,6 +60,7 @@ DoWaitPendingRun, ) +logger = logging.getLogger(__name__) T = TypeVar("T") I = TypeVar("I") @@ -448,8 +450,7 @@ async def must_take_notification(self, handle): if isinstance(res, Exception): # We might need to write out something at this point. await self.take_and_send_output() - # Print this exception, might be relevant for the user - traceback.print_exception(res) + logger.exception("Exception in take_notification", exc_info=res) raise SdkInternalException() from res if isinstance(res, Suspended): # We might need to write out something at this point. @@ -469,8 +470,7 @@ async def create_poll_or_cancel_coroutine(self, handles: typing.List[int]) -> No await self.take_and_send_output() do_progress_response = self.vm.do_progress(handles) if isinstance(do_progress_response, BaseException): - # Print this exception, might be relevant for the user - traceback.print_exception(do_progress_response) + logger.exception("Exception in do_progress", exc_info=do_progress_response) raise SdkInternalException() from do_progress_response if isinstance(do_progress_response, Suspended): raise SuspendedException()