itssKarthiii's picture
Upload 70 files
6b408d7 verified
"""
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()
# Convert string log level to logging constant
log_level = getattr(logging, settings.LOG_LEVEL.upper(), logging.INFO)
# Common processors for all output formats
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":
# JSON format for production
processors: list[Processor] = shared_processors + [
structlog.processors.format_exc_info,
structlog.processors.JSONRenderer(),
]
else:
# Console format for development
processors = shared_processors + [
structlog.dev.ConsoleRenderer(colors=True),
]
# Configure structlog
structlog.configure(
processors=processors,
wrapper_class=structlog.stdlib.BoundLogger,
context_class=dict,
logger_factory=structlog.stdlib.LoggerFactory(),
cache_logger_on_first_use=True,
)
# Configure standard library logging
logging.basicConfig(
format="%(message)s",
stream=sys.stdout,
level=log_level,
)
# Set log levels for noisy libraries
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,
)