112 lines
3.5 KiB
Python
112 lines
3.5 KiB
Python
"""
|
|
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))
|