refactor: simplify database logging handler

- Remove retry logic from DatabaseLogHandler (was for SQLite locking)
- Streamline error handling in log emission
- Clean up platform health service

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-13 20:37:34 +01:00
parent 7a7b612519
commit 44832b3fc9
3 changed files with 61 additions and 88 deletions

View File

@@ -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

View File

@@ -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():

View File

@@ -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