first commit

This commit is contained in:
2025-11-30 00:07:24 +01:00
commit b5e642aecb
78 changed files with 15162 additions and 0 deletions

255
app/core/logging_config.py Normal file
View File

@ -0,0 +1,255 @@
"""
Logging Configuration
Provides structured logging setup for the application.
Based on Python Quick Start Guide best practices.
"""
import logging
import sys
from typing import Optional
from datetime import datetime
def setup_logging(
log_level: str = "INFO",
environment: str = "development"
) -> None:
"""
Configure application logging.
Args:
log_level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
environment: Environment name (development, production)
Usage:
setup_logging(log_level="INFO", environment="production")
"""
level = getattr(logging, log_level.upper(), logging.INFO)
# Clear existing handlers
root_logger = logging.getLogger()
for handler in root_logger.handlers[:]:
root_logger.removeHandler(handler)
# Console handler
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(level)
# Format based on environment
if environment == "development":
# Human-readable format for development
formatter = logging.Formatter(
fmt='%(asctime)s | %(levelname)-8s | %(name)s:%(funcName)s:%(lineno)d | %(message)s',
datefmt='%H:%M:%S'
)
else:
# Structured format for production (easier to parse)
formatter = logging.Formatter(
fmt='%(asctime)s | %(levelname)s | %(name)s | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
console_handler.setFormatter(formatter)
root_logger.addHandler(console_handler)
root_logger.setLevel(level)
# Suppress noisy loggers
logging.getLogger('werkzeug').setLevel(logging.WARNING)
logging.getLogger('urllib3').setLevel(logging.WARNING)
def get_logger(name: str) -> logging.Logger:
"""
Get a logger instance.
Args:
name: Logger name (typically __name__)
Returns:
Logger instance
Example:
logger = get_logger(__name__)
logger.info("User logged in", extra={"user_id": 123})
"""
return logging.getLogger(name)
# ==========================================
# Structured Logging Helpers
# ==========================================
def log_audit_event(
logger: logging.Logger,
action: str,
user_id: Optional[int] = None,
ip_address: Optional[str] = None,
**kwargs
) -> None:
"""
Log an audit event with structured data.
Args:
logger: Logger instance
action: Action performed (e.g., "login_success", "user_created")
user_id: User ID performing action
ip_address: IP address of request
**kwargs: Additional context data
Example:
log_audit_event(
logger,
action="login_success",
user_id=123,
ip_address="192.168.1.1",
username="admin"
)
"""
extra_data = {
'action': action,
'user_id': user_id,
'ip_address': ip_address,
'timestamp': datetime.utcnow().isoformat(),
**kwargs
}
# Filter out None values
extra_data = {k: v for k, v in extra_data.items() if v is not None}
logger.info(f"AUDIT: {action}", extra=extra_data)
def log_security_event(
logger: logging.Logger,
event_type: str,
severity: str = "warning",
**kwargs
) -> None:
"""
Log a security-related event.
Args:
logger: Logger instance
event_type: Type of security event (e.g., "failed_login", "rate_limit_exceeded")
severity: Severity level (debug, info, warning, error, critical)
**kwargs: Additional context data
Example:
log_security_event(
logger,
event_type="failed_login",
severity="warning",
username="admin",
ip_address="192.168.1.1",
attempts=3
)
"""
extra_data = {
'event_type': event_type,
'timestamp': datetime.utcnow().isoformat(),
**kwargs
}
log_method = getattr(logger, severity.lower(), logger.warning)
log_method(f"SECURITY: {event_type}", extra=extra_data)
# ==========================================
# Request Logging Helpers
# ==========================================
def log_request(
logger: logging.Logger,
method: str,
path: str,
status_code: int,
duration_ms: float,
user_id: Optional[int] = None
) -> None:
"""
Log an HTTP request.
Args:
logger: Logger instance
method: HTTP method (GET, POST, etc.)
path: Request path
status_code: HTTP status code
duration_ms: Request duration in milliseconds
user_id: Authenticated user ID (if any)
Example:
log_request(
logger,
method="POST",
path="/api/users",
status_code=201,
duration_ms=45.2,
user_id=123
)
"""
extra_data = {
'method': method,
'path': path,
'status_code': status_code,
'duration_ms': round(duration_ms, 2),
'user_id': user_id
}
# Filter out None values
extra_data = {k: v for k, v in extra_data.items() if v is not None}
# Use different log levels based on status code
if status_code >= 500:
logger.error(f"{method} {path} {status_code}", extra=extra_data)
elif status_code >= 400:
logger.warning(f"{method} {path} {status_code}", extra=extra_data)
else:
logger.info(f"{method} {path} {status_code}", extra=extra_data)
# ==========================================
# Error Logging Helpers
# ==========================================
def log_exception(
logger: logging.Logger,
error: Exception,
context: Optional[str] = None,
**kwargs
) -> None:
"""
Log an exception with context.
Args:
logger: Logger instance
error: Exception instance
context: Additional context about where error occurred
**kwargs: Additional context data
Example:
try:
# Some operation
except Exception as e:
log_exception(
logger,
error=e,
context="Failed to create user",
user_id=123
)
"""
extra_data = {
'error_type': type(error).__name__,
'error_message': str(error),
'context': context,
**kwargs
}
# Filter out None values
extra_data = {k: v for k, v in extra_data.items() if v is not None}
logger.error(
f"Exception: {type(error).__name__}: {str(error)}",
extra=extra_data,
exc_info=True
)