diff --git a/app/core/lifespan.py b/app/core/lifespan.py index a4e9dddb..9c5d35e7 100644 --- a/app/core/lifespan.py +++ b/app/core/lifespan.py @@ -47,9 +47,12 @@ def check_database_ready(): """Check if database is ready (migrations have been run).""" try: with engine.connect() as conn: - # Try to query a table that should exist + # Check for tables in the public schema (PostgreSQL) result = conn.execute( - text("SELECT name FROM sqlite_master WHERE type='table' LIMIT 1") + text( + "SELECT tablename FROM pg_catalog.pg_tables " + "WHERE schemaname = 'public' LIMIT 1" + ) ) tables = result.fetchall() return len(tables) > 0 diff --git a/app/core/logging.py b/app/core/logging.py index 3066f84e..b28adfe7 100644 --- a/app/core/logging.py +++ b/app/core/logging.py @@ -23,92 +23,76 @@ class DatabaseLogHandler(logging.Handler): Custom logging handler that stores WARNING, ERROR, and CRITICAL logs in database. Runs asynchronously to avoid blocking application performance. - Uses retry logic for SQLite database locking issues. """ - MAX_RETRIES = 3 - RETRY_DELAY = 0.1 # 100ms delay between retries - def __init__(self): super().__init__() self.setLevel(logging.WARNING) # Only log WARNING and above to database def emit(self, record): - """Emit a log record to the database with retry logic for SQLite locking.""" - import time + """Emit a log record to the database.""" + try: + from app.core.database import SessionLocal + from models.database.admin import ApplicationLog + + # Skip if no database session available + db = SessionLocal() + if not db: + return - for attempt in range(self.MAX_RETRIES): try: - from app.core.database import SessionLocal - from models.database.admin import ApplicationLog + # Extract exception information if present + exception_type = None + exception_message = None + stack_trace = None - # Skip if no database session available - db = SessionLocal() - if not db: - return - - try: - # Extract exception information if present - exception_type = None - exception_message = None - stack_trace = None - - if record.exc_info: - exception_type = ( - record.exc_info[0].__name__ if record.exc_info[0] else None - ) - exception_message = ( - str(record.exc_info[1]) if record.exc_info[1] else None - ) - stack_trace = "".join( - traceback.format_exception(*record.exc_info) - ) - - # Extract context from record (if middleware added it) - user_id = getattr(record, "user_id", None) - vendor_id = getattr(record, "vendor_id", None) - request_id = getattr(record, "request_id", None) - context = getattr(record, "context", None) - - # Create log entry - log_entry = ApplicationLog( - timestamp=datetime.fromtimestamp(record.created, tz=UTC), - level=record.levelname, - logger_name=record.name, - module=record.module, - function_name=record.funcName, - line_number=record.lineno, - message=record.getMessage(), - exception_type=exception_type, - exception_message=exception_message, - stack_trace=stack_trace, - request_id=request_id, - user_id=user_id, - vendor_id=vendor_id, - context=context, + if record.exc_info: + exception_type = ( + record.exc_info[0].__name__ if record.exc_info[0] else None + ) + exception_message = ( + str(record.exc_info[1]) if record.exc_info[1] else None + ) + stack_trace = "".join( + traceback.format_exception(*record.exc_info) ) - db.add(log_entry) - db.commit() - return # Success, exit retry loop + # Extract context from record (if middleware added it) + user_id = getattr(record, "user_id", None) + vendor_id = getattr(record, "vendor_id", None) + request_id = getattr(record, "request_id", None) + context = getattr(record, "context", None) - except Exception as e: - db.rollback() - # Check if it's a database locked error - if "database is locked" in str(e).lower(): - if attempt < self.MAX_RETRIES - 1: - time.sleep(self.RETRY_DELAY * (attempt + 1)) - continue - # For other errors or final attempt, silently skip - # Don't print to stderr to avoid log spam during imports - finally: - db.close() + # Create log entry + log_entry = ApplicationLog( + timestamp=datetime.fromtimestamp(record.created, tz=UTC), + level=record.levelname, + logger_name=record.name, + module=record.module, + function_name=record.funcName, + line_number=record.lineno, + message=record.getMessage(), + exception_type=exception_type, + exception_message=exception_message, + stack_trace=stack_trace, + request_id=request_id, + user_id=user_id, + vendor_id=vendor_id, + context=context, + ) + + db.add(log_entry) + db.commit() except Exception: - # Silently fail - logging should never crash the app - pass + db.rollback() + # Silently skip - don't print to stderr to avoid log spam + finally: + db.close() - break # Exit retry loop on non-recoverable errors + except Exception: + # Silently fail - logging should never crash the app + pass def get_log_level_from_db(): diff --git a/app/services/platform_health_service.py b/app/services/platform_health_service.py index 30a5363b..479e957a 100644 --- a/app/services/platform_health_service.py +++ b/app/services/platform_health_service.py @@ -305,27 +305,13 @@ class PlatformHealthService: def _get_database_size(self, db: Session) -> float: """Get database size in MB.""" try: - # Try SQLite approach - result = db.execute( - text( - "SELECT page_count * page_size as size " - "FROM pragma_page_count(), pragma_page_size()" - ) - ) - row = result.fetchone() - if row: - return round(row[0] / (1024 * 1024), 2) - except Exception: - pass - - try: - # Try PostgreSQL approach result = db.execute(text("SELECT pg_database_size(current_database())")) row = result.fetchone() if row: return round(row[0] / (1024 * 1024), 2) except Exception: - pass + logger.warning("Failed to get database size") + return 0.0 return 0.0