Files
ALwrity/backend/utils/logger_utils.py

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))