TradingAgents/tradingagents/logging_config.py

133 lines
3.8 KiB
Python

"""Logging configuration for the TradingAgents framework.
This module provides structured logging setup for consistent log formatting
across all framework components.
"""
import logging
import sys
from datetime import datetime, timezone
from pathlib import Path
def setup_logging(
level: str = "INFO",
log_file: Path | None = None,
name: str = "tradingagents",
) -> logging.Logger:
"""Configure logging for the trading agents framework.
Args:
level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL).
log_file: Optional file path for log output.
name: Logger name, defaults to 'tradingagents'.
Returns:
Configured logger instance.
Example:
>>> logger = setup_logging("DEBUG", Path("logs/trading.log"))
>>> logger.info("Starting trading analysis")
"""
logger = logging.getLogger(name)
logger.setLevel(getattr(logging, level.upper()))
# Remove existing handlers to avoid duplicates
logger.handlers = []
formatter = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
# Console handler
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
# File handler (optional)
if log_file:
log_file = Path(log_file)
log_file.parent.mkdir(parents=True, exist_ok=True)
file_handler = logging.FileHandler(log_file)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
return logger
def get_logger(name: str) -> logging.Logger:
"""Get a logger with the given name.
Args:
name: Logger name (typically __name__ of the module).
Returns:
Logger instance.
Example:
>>> logger = get_logger(__name__)
>>> logger.info("Module loaded")
"""
return logging.getLogger(name)
class TradingAgentsLogger:
"""Context manager for logging trading operations.
Provides structured logging with timing information for operations.
Example:
>>> with TradingAgentsLogger("market_analysis", "AAPL") as log:
... # Perform analysis
... log.info("Fetching market data")
"""
def __init__(self, operation: str, symbol: str | None = None):
"""Initialize the logger context.
Args:
operation: Name of the operation being logged.
symbol: Optional trading symbol being analyzed.
"""
self.operation = operation
self.symbol = symbol
self.logger = get_logger(f"tradingagents.{operation}")
self.start_time: datetime | None = None
def __enter__(self) -> "TradingAgentsLogger":
"""Enter the logging context."""
self.start_time = datetime.now(timezone.utc)
msg = f"Starting {self.operation}"
if self.symbol:
msg += f" for {self.symbol}"
self.logger.info(msg)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Exit the logging context."""
if self.start_time:
duration = (datetime.now(timezone.utc) - self.start_time).total_seconds()
if exc_type:
self.logger.error(
f"{self.operation} failed after {duration:.2f}s: {exc_val}"
)
else:
self.logger.info(f"{self.operation} completed in {duration:.2f}s")
def info(self, message: str):
"""Log an info message."""
self.logger.info(message)
def warning(self, message: str):
"""Log a warning message."""
self.logger.warning(message)
def error(self, message: str):
"""Log an error message."""
self.logger.error(message)
def debug(self, message: str):
"""Log a debug message."""
self.logger.debug(message)