CodeRAG / src /coderag /logging.py
Sebastiangmz's picture
Initial CodeRAG deploy
d557d77
raw
history blame
3.26 kB
"""Structured logging configuration using structlog."""
import logging
import sys
from typing import Any
import structlog
from structlog.types import Processor
def setup_logging(
level: str = "INFO",
json_format: bool = False,
log_file: str | None = None,
) -> None:
"""Configure structured logging for the application.
Args:
level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
json_format: If True, output logs as JSON (for production)
log_file: Optional file path for logging output
"""
# Configure standard library logging
logging.basicConfig(
format="%(message)s",
stream=sys.stdout,
level=getattr(logging, level.upper()),
)
# Add file handler if specified
if log_file:
file_handler = logging.FileHandler(log_file)
file_handler.setLevel(getattr(logging, level.upper()))
logging.getLogger().addHandler(file_handler)
# Shared processors for all outputs
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 json_format:
# Production: JSON output
processors: list[Processor] = [
*shared_processors,
structlog.processors.format_exc_info,
structlog.processors.JSONRenderer(),
]
else:
# Development: Colored console output
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,
)
def get_logger(name: str | None = None) -> structlog.stdlib.BoundLogger:
"""Get a structured logger instance.
Args:
name: Logger name (usually __name__ of the calling module)
Returns:
Configured structlog logger
"""
return structlog.get_logger(name)
class LogContext:
"""Context manager for adding temporary context to logs."""
def __init__(self, **kwargs: Any) -> None:
"""Initialize with context variables."""
self.context = kwargs
self._token: Any = None
def __enter__(self) -> "LogContext":
"""Bind context variables."""
self._token = structlog.contextvars.bind_contextvars(**self.context)
return self
def __exit__(self, *args: Any) -> None:
"""Unbind context variables."""
structlog.contextvars.unbind_contextvars(*self.context.keys())
def log_operation(
operation: str,
**kwargs: Any,
) -> LogContext:
"""Create a logging context for an operation.
Usage:
with log_operation("indexing", repo_id="123"):
# All logs within this block will include repo_id
logger.info("Starting indexing")
"""
return LogContext(operation=operation, **kwargs)