ALM-2 / backend /logging_config.py
ACA050's picture
Upload 520 files
2ed8996 verified
Raw
History Blame Contribute Delete
11.3 kB
"""
Comprehensive Logging Configuration for AegisLM Backend.
Provides structured logging with multiple handlers, log levels,
and monitoring integration for production deployment.
"""
import logging
import logging.handlers
import json
import sys
from datetime import datetime
from pathlib import Path
from typing import Dict, Any
import os
from core.config import settings
class StructuredFormatter(logging.Formatter):
"""
Structured JSON formatter for consistent log formatting.
"""
def format(self, record: logging.LogRecord) -> str:
log_data = {
'timestamp': datetime.utcnow().isoformat(),
'level': record.levelname,
'logger': record.name,
'message': record.getMessage(),
'module': record.module,
'function': record.funcName,
'line': record.lineno,
'thread': record.thread,
'process': record.process
}
# Add exception information if present
if record.exc_info:
log_data['exception'] = self.formatException(record.exc_info)
# Add extra fields
for key, value in record.__dict__.items():
if key not in ['name', 'msg', 'args', 'levelname', 'levelno', 'pathname',
'filename', 'module', 'lineno', 'funcName', 'created', 'msecs',
'relativeCreated', 'thread', 'threadName', 'processName',
'process', 'getMessage', 'exc_info', 'exc_text', 'stack_info']:
log_data[key] = value
return json.dumps(log_data)
class ColoredFormatter(logging.Formatter):
"""
Colored formatter for console output during development.
"""
COLORS = {
'DEBUG': '\033[36m', # Cyan
'INFO': '\033[32m', # Green
'WARNING': '\033[33m', # Yellow
'ERROR': '\033[31m', # Red
'CRITICAL': '\033[35m', # Magenta
}
RESET = '\033[0m'
def format(self, record: logging.LogRecord) -> str:
color = self.COLORS.get(record.levelname, self.RESET)
reset = self.RESET
# Format the basic log message
formatted = super().format(record)
# Add color to the level name
return formatted.replace(
f"[{record.levelname}]",
f"[{color}{record.levelname}{reset}]"
)
def setup_logging():
"""
Setup comprehensive logging configuration.
"""
# Create logs directory if it doesn't exist
logs_dir = Path("logs")
logs_dir.mkdir(exist_ok=True)
# Root logger configuration
root_logger = logging.getLogger()
root_logger.setLevel(getattr(logging, settings.LOG_LEVEL.upper(), logging.INFO))
# Clear existing handlers
root_logger.handlers.clear()
# Console handler (colored for development)
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.DEBUG)
if settings.DEBUG:
console_formatter = ColoredFormatter(
fmt='[%(levelname)s] %(asctime)s - %(name)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
else:
console_formatter = StructuredFormatter()
console_handler.setFormatter(console_formatter)
root_logger.addHandler(console_handler)
# File handler for all logs (rotating)
file_handler = logging.handlers.RotatingFileHandler(
filename=logs_dir / "aegislm.log",
maxBytes=50 * 1024 * 1024, # 50MB
backupCount=5
)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(StructuredFormatter())
root_logger.addHandler(file_handler)
# Error file handler (only errors and above)
error_handler = logging.handlers.RotatingFileHandler(
filename=logs_dir / "errors.log",
maxBytes=10 * 1024 * 1024, # 10MB
backupCount=3
)
error_handler.setLevel(logging.ERROR)
error_handler.setFormatter(StructuredFormatter())
root_logger.addHandler(error_handler)
# Security audit log handler
security_handler = logging.handlers.RotatingFileHandler(
filename=logs_dir / "security.log",
maxBytes=10 * 1024 * 1024, # 10MB
backupCount=3
)
security_handler.setLevel(logging.INFO)
security_handler.setFormatter(StructuredFormatter())
# Create security logger
security_logger = logging.getLogger('security')
security_logger.addHandler(security_handler)
security_logger.propagate = False # Don't propagate to root logger
# Performance monitoring logger
performance_handler = logging.handlers.RotatingFileHandler(
filename=logs_dir / "performance.log",
maxBytes=20 * 1024 * 1024, # 20MB
backupCount=3
)
performance_handler.setLevel(logging.INFO)
performance_handler.setFormatter(StructuredFormatter())
# Create performance logger
performance_logger = logging.getLogger('performance')
performance_logger.addHandler(performance_handler)
performance_logger.propagate = False # Don't propagate to root logger
# Suppress noisy third-party loggers
logging.getLogger('uvicorn.access').setLevel(logging.WARNING)
logging.getLogger('uvicorn.error').setLevel(logging.WARNING)
logging.getLogger('sqlalchemy.engine').setLevel(logging.WARNING)
logging.getLogger('httpx').setLevel(logging.WARNING)
# Log startup
logger = logging.getLogger(__name__)
logger.info(
"Logging system initialized",
extra={
'log_level': settings.LOG_LEVEL,
'debug_mode': settings.DEBUG,
'environment': getattr(settings, 'ENVIRONMENT', 'development')
}
)
def get_logger(name: str) -> logging.Logger:
"""
Get a logger instance with the specified name.
Args:
name: Logger name (typically __name__)
Returns:
Configured logger instance
"""
return logging.getLogger(name)
def log_security_event(event_type: str, details: Dict[str, Any]):
"""
Log a security event.
Args:
event_type: Type of security event
details: Event details
"""
security_logger = logging.getLogger('security')
security_logger.info(
f"Security event: {event_type}",
extra={
'event_type': event_type,
'category': 'security',
**details
}
)
def log_performance_metric(metric_name: str, value: float, unit: str = 'ms', **kwargs):
"""
Log a performance metric.
Args:
metric_name: Name of the metric
value: Metric value
unit: Unit of measurement
**kwargs: Additional metadata
"""
performance_logger = logging.getLogger('performance')
performance_logger.info(
f"Performance metric: {metric_name} = {value} {unit}",
extra={
'metric_name': metric_name,
'metric_value': value,
'metric_unit': unit,
'category': 'performance',
**kwargs
}
)
def log_api_request(
method: str,
path: str,
status_code: int,
response_time: float,
user_id: int = None,
ip_address: str = None
):
"""
Log an API request for monitoring.
Args:
method: HTTP method
path: Request path
status_code: Response status code
response_time: Response time in milliseconds
user_id: User ID if authenticated
ip_address: Client IP address
"""
logger = get_logger('api')
log_level = logging.WARNING if status_code >= 400 else logging.INFO
logger.log(
log_level,
f"API Request: {method} {path} - {status_code} - {response_time:.2f}ms",
extra={
'method': method,
'path': path,
'status_code': status_code,
'response_time_ms': response_time,
'user_id': user_id,
'ip_address': ip_address,
'category': 'api_request'
}
)
def log_evaluation_event(
evaluation_id: str,
event_type: str,
details: Dict[str, Any],
user_id: int = None
):
"""
Log an evaluation-related event.
Args:
evaluation_id: Evaluation ID
event_type: Type of event (started, completed, failed, etc.)
details: Event details
user_id: User ID
"""
logger = get_logger('evaluation')
logger.info(
f"Evaluation {event_type}: {evaluation_id}",
extra={
'evaluation_id': evaluation_id,
'event_type': event_type,
'user_id': user_id,
'category': 'evaluation',
**details
}
)
def log_system_event(
event_type: str,
message: str,
level: str = 'info',
**kwargs
):
"""
Log a system-level event.
Args:
event_type: Type of system event
message: Event message
level: Log level (info, warning, error)
**kwargs: Additional metadata
"""
logger = get_logger('system')
log_method = getattr(logger, level.lower())
log_method(
f"System event: {event_type} - {message}",
extra={
'event_type': event_type,
'category': 'system',
**kwargs
}
)
class RequestLogger:
"""
Middleware for logging HTTP requests and responses.
"""
def __init__(self):
self.logger = get_logger('http_requests')
async def log_request(self, request, response_time: float, status_code: int):
"""Log HTTP request details."""
await log_api_request(
method=request.method,
path=request.url.path,
status_code=status_code,
response_time=response_time,
ip_address=request.client.host if request.client else None
)
# Context manager for performance logging
class PerformanceTimer:
"""
Context manager for timing operations and logging performance metrics.
"""
def __init__(self, operation_name: str, **metadata):
self.operation_name = operation_name
self.metadata = metadata
self.start_time = None
self.logger = get_logger('performance')
def __enter__(self):
self.start_time = datetime.utcnow()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if self.start_time:
duration = (datetime.utcnow() - self.start_time).total_seconds() * 1000
log_performance_metric(
metric_name=self.operation_name,
value=duration,
unit='ms',
**self.metadata
)
def performance_timer(operation_name: str, **metadata):
"""
Decorator for timing function execution.
Args:
operation_name: Name of the operation being timed
**metadata: Additional metadata to log
"""
def decorator(func):
def wrapper(*args, **kwargs):
with PerformanceTimer(operation_name, **metadata):
return func(*args, **kwargs)
return wrapper
return decorator
# Initialize logging when module is imported
setup_logging()