198 lines
5.7 KiB
Python
198 lines
5.7 KiB
Python
"""
|
|
Logging configuration for analysis scripts
|
|
Provides structured logging with file and console output
|
|
|
|
Usage:
|
|
from logger_config import get_logger
|
|
|
|
logger = get_logger('my_analysis')
|
|
logger.info("Analysis started")
|
|
logger.warning("Low data quality detected")
|
|
logger.error("Failed to load data")
|
|
"""
|
|
import logging
|
|
import sys
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
from config import COMPANY_NAME, OUTPUT_DIR
|
|
|
|
# Global logger instance
|
|
_logger = None
|
|
|
|
def setup_logging(log_level=logging.INFO, log_file=None, analysis_name=None):
|
|
"""
|
|
Setup logging configuration
|
|
|
|
Args:
|
|
log_level: Logging level (DEBUG, INFO, WARNING, ERROR)
|
|
log_file: Path to log file (defaults to logs/analysis_YYYYMMDD_HHMMSS.log)
|
|
analysis_name: Name of analysis for log file naming
|
|
|
|
Returns:
|
|
logging.Logger: Configured logger instance
|
|
"""
|
|
global _logger
|
|
|
|
# Create logs directory
|
|
logs_dir = Path('logs')
|
|
logs_dir.mkdir(exist_ok=True)
|
|
|
|
# Default log file name
|
|
if log_file is None:
|
|
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
|
if analysis_name:
|
|
safe_name = analysis_name.lower().replace(' ', '_').replace('/', '_')
|
|
log_file = logs_dir / f"{safe_name}_{timestamp}.log"
|
|
else:
|
|
log_file = logs_dir / f"analysis_{timestamp}.log"
|
|
else:
|
|
log_file = Path(log_file)
|
|
log_file.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Create logger
|
|
logger = logging.getLogger(analysis_name or 'analysis')
|
|
logger.setLevel(log_level)
|
|
|
|
# Remove existing handlers to avoid duplicates
|
|
logger.handlers = []
|
|
|
|
# Create formatters
|
|
detailed_formatter = logging.Formatter(
|
|
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
datefmt='%Y-%m-%d %H:%M:%S'
|
|
)
|
|
|
|
console_formatter = logging.Formatter(
|
|
'%(levelname)s - %(message)s'
|
|
)
|
|
|
|
# File handler (detailed)
|
|
file_handler = logging.FileHandler(log_file, encoding='utf-8')
|
|
file_handler.setLevel(log_level)
|
|
file_handler.setFormatter(detailed_formatter)
|
|
logger.addHandler(file_handler)
|
|
|
|
# Console handler (simpler)
|
|
console_handler = logging.StreamHandler(sys.stdout)
|
|
console_handler.setLevel(log_level)
|
|
console_handler.setFormatter(console_formatter)
|
|
logger.addHandler(console_handler)
|
|
|
|
# Log startup message
|
|
logger.info(f"="*60)
|
|
logger.info(f"Analysis: {analysis_name or 'Unknown'}")
|
|
logger.info(f"Company: {COMPANY_NAME}")
|
|
logger.info(f"Log File: {log_file}")
|
|
logger.info(f"="*60)
|
|
|
|
_logger = logger
|
|
return logger
|
|
|
|
def get_logger(analysis_name=None, log_level=logging.INFO):
|
|
"""
|
|
Get or create logger instance
|
|
|
|
Args:
|
|
analysis_name: Name of analysis
|
|
log_level: Logging level (default: INFO)
|
|
|
|
Returns:
|
|
logging.Logger: Logger instance
|
|
"""
|
|
global _logger
|
|
|
|
if _logger is None:
|
|
_logger = setup_logging(log_level=log_level, analysis_name=analysis_name)
|
|
|
|
return _logger
|
|
|
|
def log_analysis_start(analysis_name, logger=None):
|
|
"""
|
|
Log analysis start
|
|
|
|
Args:
|
|
analysis_name: Name of analysis
|
|
logger: Logger instance (creates one if None)
|
|
"""
|
|
if logger is None:
|
|
logger = get_logger(analysis_name)
|
|
|
|
logger.info(f"Starting analysis: {analysis_name}")
|
|
logger.info(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
|
|
def log_analysis_end(analysis_name, success=True, logger=None):
|
|
"""
|
|
Log analysis completion
|
|
|
|
Args:
|
|
analysis_name: Name of analysis
|
|
success: Whether analysis completed successfully
|
|
logger: Logger instance (creates one if None)
|
|
"""
|
|
if logger is None:
|
|
logger = get_logger(analysis_name)
|
|
|
|
if success:
|
|
logger.info(f"Analysis completed successfully: {analysis_name}")
|
|
else:
|
|
logger.error(f"Analysis failed: {analysis_name}")
|
|
|
|
logger.info(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
logger.info("="*60)
|
|
|
|
def log_data_loading(df, logger=None):
|
|
"""
|
|
Log data loading summary
|
|
|
|
Args:
|
|
df: Loaded DataFrame
|
|
logger: Logger instance (creates one if None)
|
|
"""
|
|
if logger is None:
|
|
logger = get_logger()
|
|
|
|
logger.info(f"Data loaded: {len(df):,} rows, {len(df.columns)} columns")
|
|
|
|
from config import REVENUE_COLUMN, DATE_COLUMN
|
|
if REVENUE_COLUMN in df.columns:
|
|
revenue = df[REVENUE_COLUMN].sum()
|
|
logger.info(f"Total revenue: ${revenue / 1e6:.2f}m")
|
|
|
|
if DATE_COLUMN in df.columns:
|
|
date_coverage = df[DATE_COLUMN].notna().sum() / len(df) * 100
|
|
logger.info(f"Date coverage: {date_coverage:.1f}%")
|
|
|
|
def log_error(error, logger=None, context=None):
|
|
"""
|
|
Log error with context
|
|
|
|
Args:
|
|
error: Exception or error message
|
|
logger: Logger instance (creates one if None)
|
|
context: Additional context string
|
|
"""
|
|
if logger is None:
|
|
logger = get_logger()
|
|
|
|
error_msg = str(error)
|
|
if context:
|
|
error_msg = f"{context}: {error_msg}"
|
|
|
|
logger.error(error_msg, exc_info=True)
|
|
|
|
# ============================================================================
|
|
# EXAMPLE USAGE
|
|
# ============================================================================
|
|
|
|
if __name__ == "__main__":
|
|
"""Example usage"""
|
|
logger = setup_logging(log_level=logging.DEBUG, analysis_name="Example Analysis")
|
|
|
|
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")
|
|
|
|
log_analysis_start("Example Analysis", logger)
|
|
log_analysis_end("Example Analysis", success=True, logger)
|