""" Core - Structured Logging JSON structured logging with correlation IDs. """ import logging import sys from contextvars import ContextVar from datetime import datetime from typing import Any, Dict import structlog # Correlation ID context variable correlation_id_var: ContextVar[str] = ContextVar("correlation_id", default="") def get_correlation_id() -> str: """Get current correlation ID""" return correlation_id_var.get() def set_correlation_id(correlation_id: str) -> None: """Set correlation ID for current context""" correlation_id_var.set(correlation_id) def add_correlation_id(logger: Any, method_name: str, event_dict: Dict) -> Dict: """Add correlation ID to log event""" event_dict["correlation_id"] = get_correlation_id() return event_dict def add_service_info(logger: Any, method_name: str, event_dict: Dict) -> Dict: """Add service information to log event""" event_dict["service"] = "rag-onboarding-backend" event_dict["version"] = "1.0.0" return event_dict def setup_logging(log_level: str = "INFO") -> None: """Setup structured logging""" # Configure structlog structlog.configure( processors=[ structlog.contextvars.merge_contextvars, structlog.stdlib.filter_by_level, structlog.processors.TimeStamper(fmt="iso"), structlog.stdlib.add_logger_name, structlog.stdlib.add_log_level, structlog.processors.StackInfoRenderer(), add_correlation_id, add_service_info, structlog.processors.format_exc_info, structlog.processors.UnicodeDecoder(), structlog.processors.JSONRenderer(), ], wrapper_class=structlog.stdlib.BoundLogger, context_class=dict, logger_factory=structlog.stdlib.LoggerFactory(), cache_logger_on_first_use=True, ) # Configure standard logging logging.basicConfig( format="%(message)s", stream=sys.stdout, level=getattr(logging, log_level.upper()), ) def get_logger(name: str) -> structlog.stdlib.BoundLogger: """Get logger instance""" return structlog.get_logger(name)