AGI / logger.py
Dmitry Beresnev
fix gitignore, app and logger, etc
7763bf4
"""
Centralized logging module for AGI Multi-Model API.
Provides structured logging with:
- Colored console output
- File logging with rotation
- Configurable log levels
- Timestamp and module name tracking
"""
import logging
import sys
from pathlib import Path
from logging.handlers import RotatingFileHandler
from typing import Optional
class ColoredFormatter(logging.Formatter):
"""Custom formatter with color support for console output."""
# ANSI color codes
COLORS = {
'DEBUG': '\033[36m', # Cyan
'INFO': '\033[32m', # Green
'WARNING': '\033[33m', # Yellow
'ERROR': '\033[31m', # Red
'CRITICAL': '\033[35m', # Magenta
}
RESET = '\033[0m'
BOLD = '\033[1m'
def format(self, record):
"""Format log record with colors."""
# Add color to level name
levelname = record.levelname
if levelname in self.COLORS:
record.levelname = f"{self.COLORS[levelname]}{self.BOLD}{levelname}{self.RESET}"
# Format the message
result = super().format(record)
# Reset levelname for other handlers
record.levelname = levelname
return result
class Logger:
"""
Singleton logger class for the entire application.
Usage:
from logger import get_logger
logger = get_logger(__name__)
logger.info("Application started")
"""
_instance: Optional[logging.Logger] = None
_initialized: bool = False
@classmethod
def get_logger(
cls,
name: str = "AGI",
level: int = logging.INFO,
log_file: Optional[str] = "agi.log",
max_bytes: int = 10 * 1024 * 1024, # 10MB
backup_count: int = 5
) -> logging.Logger:
"""
Get or create the application logger.
Args:
name: Logger name (typically module name)
level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
log_file: Path to log file (None to disable file logging)
max_bytes: Maximum size of log file before rotation
backup_count: Number of backup files to keep
Returns:
Configured logger instance
"""
# Create or get logger
logger = logging.getLogger(name)
# Only configure handlers once for the root logger
if not cls._initialized and name == "AGI":
logger.setLevel(level)
# Console handler with colors
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(level)
console_formatter = ColoredFormatter(
fmt='%(asctime)s | %(levelname)s | %(name)s | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
console_handler.setFormatter(console_formatter)
logger.addHandler(console_handler)
# File handler with rotation (if enabled)
if log_file:
log_path = Path(log_file)
log_path.parent.mkdir(parents=True, exist_ok=True)
file_handler = RotatingFileHandler(
log_file,
maxBytes=max_bytes,
backupCount=backup_count
)
file_handler.setLevel(level)
file_formatter = logging.Formatter(
fmt='%(asctime)s | %(levelname)-8s | %(name)s | %(funcName)s:%(lineno)d | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
file_handler.setFormatter(file_formatter)
logger.addHandler(file_handler)
# Prevent propagation to avoid duplicate logs
logger.propagate = False
cls._initialized = True
return logger
# Convenience function for easy import
def get_logger(name: str = "AGI", level: int = logging.INFO) -> logging.Logger:
"""
Get a logger instance for the specified module.
Args:
name: Logger name (use __name__ for automatic module naming)
level: Logging level (default: INFO)
Returns:
Configured logger instance
Example:
from logger import get_logger
logger = get_logger(__name__)
logger.info("Starting application")
"""
return Logger.get_logger(name, level)
# Initialize the root logger on module import
_root_logger = Logger.get_logger("AGI", level=logging.INFO)
if __name__ == "__main__":
# Test the logger
logger = get_logger("test_module")
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning message")
logger.error("This is an error message")
logger.critical("This is a critical message")
print("\nTesting with different module names:")
api_logger = get_logger("api")
api_logger.info("API logger initialized")
client_logger = get_logger("client")
client_logger.info("Client logger initialized")