""" Logger utilities to prevent conflicts between different logging configurations. """ from loguru import logger import sys import hashlib import json from typing import Any, Dict, Optional def safe_logger_config(format_string: str, level: str = "INFO"): """ Safely configure logger without removing existing handlers. This prevents conflicts with the main logging configuration. Args: format_string: Log format string level: Log level """ try: # Only add a new handler if we don't already have one with this format existing_handlers = logger._core.handlers for handler in existing_handlers: if hasattr(handler, '_sink') and handler._sink == sys.stdout: # Check if format is similar to avoid duplicates if hasattr(handler, '_format') and handler._format == format_string: return # Handler already exists with this format # Add new handler only if needed logger.add( sys.stdout, level=level, format=format_string, colorize=True ) except Exception as e: # If there's any error, just use the existing logger configuration pass def get_service_logger(service_name: str, format_string: str = None): """ Get a logger for a specific service without conflicting with main configuration. Args: service_name: Name of the service format_string: Optional custom format string Returns: Logger instance """ if format_string: safe_logger_config(format_string) return logger.bind(service=service_name) def _mask_user_id(user_id: Optional[str]) -> str: """Mask user ID for privacy in logs.""" if not user_id: return "anonymous" return hashlib.sha256(str(user_id).encode("utf-8")).hexdigest()[:12] def emit_routing_event( logger_instance, flow_type: str, *, route_intent: str = "primary", provider_selected: str, model_selected: str, preferred_provider: Optional[str] = None, fallback_count: int = 0, fallback_models_tried: Optional[list] = None, tenant_user_id: Optional[str] = None, extra: Optional[Dict[str, Any]] = None, level: str = "info" ) -> None: """ Emit structured routing event for LLM provider selection. Args: logger_instance: Logger instance to use flow_type: Type of flow (e.g., "sif_agent", "premium_tool") route_intent: Route intent ("primary" or "fallback") provider_selected: Selected provider name model_selected: Selected model name preferred_provider: Preferred provider (if any) fallback_count: Number of fallback attempts made fallback_models_tried: List of models tried as fallbacks tenant_user_id: Tenant user ID (will be hashed) extra: Additional fields to include level: Log level to use """ payload: Dict[str, Any] = { "flow_type": flow_type, "route_intent": route_intent, "provider_selected": provider_selected, "model_selected": model_selected, "preferred_provider": preferred_provider, "fallback_count": fallback_count, "fallback_models_tried": fallback_models_tried or [], "tenant": _mask_user_id(tenant_user_id), } if extra: payload.update(extra) log_method = getattr(logger_instance, level.lower(), logger_instance.info) log_method("[llm_routing] {}", json.dumps(payload, sort_keys=True, default=str))