| """
|
| Structured logging configuration for VoiceAuth API.
|
|
|
| Uses structlog for JSON-formatted logs in production
|
| and human-readable logs in development.
|
| """
|
|
|
| import logging
|
| import sys
|
| from typing import Any
|
|
|
| import structlog
|
| from structlog.types import Processor
|
|
|
| from app.config import get_settings
|
|
|
|
|
| def setup_logging() -> None:
|
| """
|
| Configure structured logging based on application settings.
|
|
|
| Sets up structlog processors for either JSON or console output
|
| based on the LOG_FORMAT setting.
|
| """
|
| settings = get_settings()
|
|
|
|
|
| log_level = getattr(logging, settings.LOG_LEVEL.upper(), logging.INFO)
|
|
|
|
|
| shared_processors: list[Processor] = [
|
| structlog.contextvars.merge_contextvars,
|
| structlog.stdlib.add_log_level,
|
| structlog.stdlib.add_logger_name,
|
| structlog.stdlib.PositionalArgumentsFormatter(),
|
| structlog.processors.TimeStamper(fmt="iso"),
|
| structlog.processors.StackInfoRenderer(),
|
| structlog.processors.UnicodeDecoder(),
|
| ]
|
|
|
| if settings.LOG_FORMAT == "json":
|
|
|
| processors: list[Processor] = shared_processors + [
|
| structlog.processors.format_exc_info,
|
| structlog.processors.JSONRenderer(),
|
| ]
|
| else:
|
|
|
| processors = shared_processors + [
|
| structlog.dev.ConsoleRenderer(colors=True),
|
| ]
|
|
|
|
|
| structlog.configure(
|
| processors=processors,
|
| wrapper_class=structlog.stdlib.BoundLogger,
|
| context_class=dict,
|
| logger_factory=structlog.stdlib.LoggerFactory(),
|
| cache_logger_on_first_use=True,
|
| )
|
|
|
|
|
| logging.basicConfig(
|
| format="%(message)s",
|
| stream=sys.stdout,
|
| level=log_level,
|
| )
|
|
|
|
|
| logging.getLogger("uvicorn").setLevel(logging.WARNING)
|
| logging.getLogger("httpx").setLevel(logging.WARNING)
|
| logging.getLogger("httpcore").setLevel(logging.WARNING)
|
|
|
|
|
| def get_logger(name: str | None = None) -> structlog.stdlib.BoundLogger:
|
| """
|
| Get a configured logger instance.
|
|
|
| Args:
|
| name: Optional logger name. If not provided, uses the calling module.
|
|
|
| Returns:
|
| A configured structlog bound logger.
|
| """
|
| return structlog.get_logger(name)
|
|
|
|
|
| class LoggerMixin:
|
| """Mixin class that provides a logger property."""
|
|
|
| @property
|
| def logger(self) -> structlog.stdlib.BoundLogger:
|
| """Get a logger bound to the class name."""
|
| return get_logger(self.__class__.__name__)
|
|
|
|
|
| def log_request(
|
| method: str,
|
| path: str,
|
| status_code: int,
|
| duration_ms: float,
|
| **kwargs: Any,
|
| ) -> None:
|
| """
|
| Log an HTTP request with structured data.
|
|
|
| Args:
|
| method: HTTP method (GET, POST, etc.)
|
| path: Request path
|
| status_code: Response status code
|
| duration_ms: Request duration in milliseconds
|
| **kwargs: Additional fields to log
|
| """
|
| logger = get_logger("http")
|
| logger.info(
|
| "http_request",
|
| method=method,
|
| path=path,
|
| status_code=status_code,
|
| duration_ms=round(duration_ms, 2),
|
| **kwargs,
|
| )
|
|
|