Files
enclava/backend/app/core/logging.py
2025-08-19 09:50:15 +02:00

153 lines
3.9 KiB
Python

"""
Logging configuration
"""
import logging
import sys
from typing import Any, Dict
import structlog
from structlog.stdlib import LoggerFactory
from app.core.config import settings
def setup_logging() -> None:
"""Setup structured logging"""
# Configure structlog
structlog.configure(
processors=[
structlog.stdlib.filter_by_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
structlog.processors.JSONRenderer() if settings.LOG_FORMAT == "json" else structlog.dev.ConsoleRenderer(),
],
context_class=dict,
logger_factory=LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
# Configure standard logging
logging.basicConfig(
format="%(message)s",
stream=sys.stdout,
level=getattr(logging, settings.LOG_LEVEL.upper()),
)
# Set specific loggers
logging.getLogger("uvicorn").setLevel(logging.WARNING)
logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING)
def get_logger(name: str) -> structlog.stdlib.BoundLogger:
"""Get a structured logger"""
return structlog.get_logger(name)
class RequestContextFilter(logging.Filter):
"""Add request context to log records"""
def filter(self, record: logging.LogRecord) -> bool:
# Add request context if available
from contextvars import ContextVar
request_id: ContextVar[str] = ContextVar("request_id", default="")
user_id: ContextVar[str] = ContextVar("user_id", default="")
record.request_id = request_id.get()
record.user_id = user_id.get()
return True
def log_request(
method: str,
path: str,
status_code: int,
processing_time: float,
user_id: str = None,
request_id: str = None,
**kwargs: Any,
) -> None:
"""Log HTTP request"""
logger = get_logger("api.request")
log_data = {
"method": method,
"path": path,
"status_code": status_code,
"processing_time": processing_time,
"user_id": user_id,
"request_id": request_id,
**kwargs,
}
if status_code >= 500:
logger.error("Request failed", **log_data)
elif status_code >= 400:
logger.warning("Request error", **log_data)
else:
logger.info("Request completed", **log_data)
def log_security_event(
event_type: str,
user_id: str = None,
ip_address: str = None,
details: Dict[str, Any] = None,
**kwargs: Any,
) -> None:
"""Log security event"""
logger = get_logger("security")
log_data = {
"event_type": event_type,
"user_id": user_id,
"ip_address": ip_address,
"details": details or {},
**kwargs,
}
logger.warning("Security event", **log_data)
def log_module_event(
module_id: str,
event_type: str,
details: Dict[str, Any] = None,
**kwargs: Any,
) -> None:
"""Log module event"""
logger = get_logger("module")
log_data = {
"module_id": module_id,
"event_type": event_type,
"details": details or {},
**kwargs,
}
logger.info("Module event", **log_data)
def log_api_request(
endpoint: str,
params: Dict[str, Any] = None,
**kwargs: Any,
) -> None:
"""Log API request for modules endpoints"""
logger = get_logger("api.module")
log_data = {
"endpoint": endpoint,
"params": params or {},
**kwargs,
}
logger.info("API request", **log_data)