ADMP-LS / servers /Review /config_logger.py
jackkuo's picture
reinit repo
82bf89e
import logging
from pathlib import Path
from typing import Optional
import sys
# Handle relative imports
try:
from .setting_config import settings
except ImportError:
from setting_config import settings
LOG_FORMAT_DETAILED: str = "%(asctime)s | %(name)s | %(levelname)s | %(filename)s:%(lineno)d | %(funcName)s | %(message)s"
LOG_FORMAT_SIMPLE: str = "%(asctime)s | %(levelname)s | %(name)s | %(message)s"
LOG_DATE_FORMAT: str = "%Y-%m-%d %H:%M:%S"
class LoggerConfig:
"""A robust logger configuration class for the Bio-Agent application."""
def __init__(self, name: str = __name__, log_level: Optional[str] = None):
self.name = name
self.log_level = log_level or settings.LOG_LEVEL.upper()
self.log_dir = Path(settings.LOG_DIR)
self.log_file = self.log_dir / f"{name}.log"
# Create logger
self.logger = logging.getLogger(name)
self.logger.setLevel(getattr(logging, self.log_level))
# Prevent duplicate handlers
if self.logger.handlers:
return
self._setup_handlers()
self._setup_formatters()
def _setup_handlers(self):
"""Setup console and file handlers."""
# Console handler
if settings.LOG_ENABLE_CONSOLE:
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.INFO)
self.logger.addHandler(console_handler)
# File handler with rotation
if settings.LOG_ENABLE_FILE:
try:
# Ensure log directory exists
self.log_dir.mkdir(parents=True, exist_ok=True)
# Rotating file handler
file_handler = logging.handlers.RotatingFileHandler(
filename=self.log_file,
maxBytes=settings.LOG_MAX_SIZE,
backupCount=settings.LOG_BACKUP_COUNT,
encoding='utf-8'
)
file_handler.setLevel(logging.DEBUG)
self.logger.addHandler(file_handler)
except Exception as e:
# Fallback to basic file handler if rotation fails
try:
file_handler = logging.FileHandler(
filename=self.log_file,
encoding='utf-8'
)
file_handler.setLevel(logging.DEBUG)
self.logger.addHandler(file_handler)
except Exception as fallback_error:
self.logger.warning(f"Failed to setup file logging: {fallback_error}")
def _setup_formatters(self):
"""Setup formatters for different handlers."""
# Detailed format for file logging
detailed_format = logging.Formatter(
fmt=LOG_FORMAT_DETAILED,
datefmt=LOG_DATE_FORMAT
)
# Simple format for console logging
simple_format = logging.Formatter(
fmt=LOG_FORMAT_SIMPLE,
datefmt='%H:%M:%S'
)
# Apply formatters to handlers
for handler in self.logger.handlers:
if isinstance(handler, logging.StreamHandler) and handler.stream == sys.stdout:
handler.setFormatter(simple_format)
else:
handler.setFormatter(detailed_format)
def get_logger(self):
"""Get the configured logger instance."""
return self.logger
# Create default logger instance
logger_config = LoggerConfig()
logger = logger_config.get_logger()
def get_logger(name: str = None, log_level: str = None) -> logging.Logger:
"""
Get a logger instance with the specified name and log level.
Args:
name: Logger name (defaults to calling module name)
log_level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
Returns:
Configured logger instance
"""
if name is None:
# Get the calling module name
import inspect
frame = inspect.currentframe().f_back
name = frame.f_globals.get('__name__', __name__)
return LoggerConfig(name, log_level).get_logger()
# Convenience functions for common logging patterns
def log_function_entry(func_name: str, **kwargs):
"""Log function entry with parameters."""
logger.debug(f"Entering {func_name} with params: {kwargs}")
def log_function_exit(func_name: str, result=None, duration=None):
"""Log function exit with result and duration."""
if duration:
logger.debug(f"Exiting {func_name} - Duration: {duration:.3f}s")
else:
logger.debug(f"Exiting {func_name}")
def log_error_with_context(error: Exception, context: str = ""):
"""Log error with additional context."""
logger.error(f"{context}: {str(error)}", exc_info=True)
# Example usage decorator
def log_function_call(func):
"""Decorator to automatically log function entry and exit."""
import functools
import time
@functools.wraps(func)
def wrapper(*args, **kwargs):
func_name = func.__name__
start_time = time.time()
log_function_entry(func_name, args=args, kwargs=kwargs)
try:
result = func(*args, **kwargs)
duration = time.time() - start_time
log_function_exit(func_name, result=result, duration=duration)
return result
except Exception as e:
duration = time.time() - start_time
log_error_with_context(e, f"Error in {func_name} after {duration:.3f}s")
raise
return wrapper
if __name__ == "__main__":
# Test the logger configuration
logger.info("Logger configuration initialized successfully")
logger.debug("This is a debug message")
logger.warning("This is a warning message")
logger.error("This is an error message")
# Test the convenience functions
test_logger = get_logger("test_module")
test_logger.info("Test logger created successfully")