import datetime import logging import os from functools import wraps from fastapi import HTTPException from fastapi.security import OAuth2PasswordBearer date = f'{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}' logger = logging.getLogger('discord_app') # date = f'{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}' # log_level = logger.info if os.environ.get('LOG_LEVEL') == 'INFO' else 'WARN' # logging.basicConfig( # filename=f'logs/database/{date}.log', # format='%(asctime)s - sba-database - %(levelname)s - %(message)s', # level=log_level # ) oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") priv_help = False if not os.environ.get('PRIVATE_IN_SCHEMA') else os.environ.get('PRIVATE_IN_SCHEMA').upper() PRIVATE_IN_SCHEMA = True if priv_help == 'TRUE' else False def valid_token(token): return token == os.environ.get('API_TOKEN') def handle_db_errors(func): """ Decorator to handle database connection errors and transaction rollbacks. Ensures proper cleanup of database connections and provides consistent error handling. Includes comprehensive logging with function context, timing, and stack traces. """ @wraps(func) async def wrapper(*args, **kwargs): import time import traceback from .db_engine import db # Import here to avoid circular imports start_time = time.time() func_name = f"{func.__module__}.{func.__name__}" # Sanitize arguments for logging (exclude sensitive data) safe_args = [] safe_kwargs = {} try: # Log sanitized arguments (avoid logging tokens, passwords, etc.) for i, arg in enumerate(args): if hasattr(arg, '__dict__') and hasattr(arg, 'url'): # FastAPI Request object safe_args.append(f"Request({getattr(arg, 'method', 'UNKNOWN')} {getattr(arg, 'url', 'unknown')})") else: safe_args.append(str(arg)[:100]) # Truncate long values for key, value in kwargs.items(): if key.lower() in ['token', 'password', 'secret', 'key']: safe_kwargs[key] = '[REDACTED]' else: safe_kwargs[key] = str(value)[:100] # Truncate long values logger.info(f"Starting {func_name} - args: {safe_args}, kwargs: {safe_kwargs}") result = await func(*args, **kwargs) elapsed_time = time.time() - start_time logger.info(f"Completed {func_name} successfully in {elapsed_time:.3f}s") return result except Exception as e: elapsed_time = time.time() - start_time error_trace = traceback.format_exc() logger.error(f"Database error in {func_name} after {elapsed_time:.3f}s") logger.error(f"Function args: {safe_args}") logger.error(f"Function kwargs: {safe_kwargs}") logger.error(f"Exception: {str(e)}") logger.error(f"Full traceback:\n{error_trace}") try: logger.info(f"Attempting database rollback for {func_name}") db.rollback() logger.info(f"Database rollback successful for {func_name}") except Exception as rollback_error: logger.error(f"Rollback failed in {func_name}: {rollback_error}") finally: try: db.close() logger.info(f"Database connection closed for {func_name}") except Exception as close_error: logger.error(f"Error closing database connection in {func_name}: {close_error}") raise HTTPException(status_code=500, detail=f'Database error in {func_name}: {str(e)}') return wrapper