import logging import colorlog import sys import time def initialize_logger(name, level=logging.WARNING): logger = logging.getLogger(name) logger.propagate = False handler = colorlog.StreamHandler(stream=sys.stdout) formatter = colorlog.ColoredFormatter( "%(log_color)s[%(asctime)s][%(levelname)s][%(module)s]:%(reset)s %(message)s", reset=True, log_colors={ "DEBUG": "cyan", "INFO": "green", "WARNING": "yellow", "ERROR": "red", "CRITICAL": "red,bg_white", }, ) handler.setFormatter(formatter) logger.addHandler(handler) logger.setLevel(level) return logger def catch_and_log_exceptions_for_sio_event_handlers(sio, logger): # wrapper should have the same signature as the original function def decorator(func): async def catch_exception_wrapper(*args, **kwargs): try: return await func(*args, **kwargs) except Exception as e: message = f"[app_pubsub] Caught exception in '{func.__name__}' event handler:\n\n{e}" logger.exception(message, stack_info=True) try: exception_data = { "message": message, "timeEpochMs": int(time.time() * 1000), } # For now let's emit this to all clients. We ultimatley may want to emit it just to the room it's happening in. await sio.emit("server_exception", exception_data) except Exception as inner_e: logger.exception( f"[app_pubsub] Caught exception while trying to emit server_exception event:\n{inner_e}" ) # Re-raise the exception so it's handled normally by the server raise e # Set the name of the wrapper to the name of the original function so that the socketio server can associate it with the right event catch_exception_wrapper.__name__ = func.__name__ return catch_exception_wrapper return decorator