import logging import time import csv import os from pathlib import Path from datetime import datetime """ Logging Utilities Module ======================== This module provides enhanced logging capabilities for the behavior analytics application. Features include: - Emoji-enhanced log messages for better visual identification - Daily log file rotation with date-based filenames - Dual logging to both console and files - Performance timing and measurement utilities - Custom log levels and formatting Usage: ------ # Basic setup with both console and file logging logger = setup_logger("my_module") # Log at different levels with automatic emoji inclusion logger.debug("Debugging information") logger.info("General information") logger.warning("Warning message") logger.error("Error occurred") logger.critical("Critical failure") # Log success messages with checkmark emoji log_success(logger, "Operation completed successfully") # Measure function execution time @time_it def my_function(): # Function code here pass """ # Configure logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) # Create logs directory if it doesn't exist logs_dir = Path("logs") logs_dir.mkdir(exist_ok=True) # Time logs file TIME_LOGS_FILE = logs_dir / "time_logs.csv" # Emoji mappings for different log levels LOG_EMOJIS = { 'DEBUG': 'debug', 'INFO': 'info', 'WARNING': 'warning', 'ERROR': 'error', 'CRITICAL': 'critical', 'SUCCESS': 'success', 'TIMER': 'timer' } def get_daily_log_filename(base_name="app"): """ Generate a log filename with the current date. The function creates a filename in the format: {base_name}_{YYYY-MM-DD}.log This ensures that logs are automatically separated by day, making it easier to manage log files and implement log rotation policies. Args: base_name (str): Base name for the log file, defaults to "app" Returns: Path: Path object for the log file with current date Example: >>> get_daily_log_filename("api") Path('logs/api_2023-11-15.log') """ today = datetime.now().strftime("%Y-%m-%d") return logs_dir / f"{base_name}_{today}.log" def setup_logger(name, log_file=None, level=logging.INFO, enable_console=True, enable_file=True): """ Set up a logger with file and console handlers. This function configures a logger with the specified name and adds handlers for console output and/or file output based on the parameters. It also adds emoji support to make logs more visually informative. Args: name (str): Logger name, typically the module name using __name__ log_file (str, optional): Path to log file. If None and enable_file is True, a daily log file will be used. Defaults to None. level (int): Logging level (e.g., logging.INFO, logging.DEBUG). Defaults to logging.INFO. enable_console (bool): Whether to enable console logging. Defaults to True. enable_file (bool): Whether to enable file logging. Defaults to True. Returns: logging.Logger: Configured logger instance Example: >>> # Basic usage with both console and file logging >>> logger = setup_logger("my_module") >>> >>> # Console only (no file logging) >>> logger = setup_logger("console_only", enable_file=False) >>> >>> # File only with custom file path >>> logger = setup_logger("file_only", log_file="custom.log", enable_console=False) """ logger = logging.getLogger(name) logger.setLevel(level) # Remove existing handlers if any for handler in logger.handlers[:]: logger.removeHandler(handler) # Format with emojis log_format = '%(asctime)s - %(name)s - %(emoji)s %(levelname)s - %(message)s' # Create a filter to add emoji to the record class EmojiFilter(logging.Filter): """ Filter that adds an emoji field to the log record based on the log level. This filter enriches log records with emojis corresponding to their log levels, making logs more visually informative and easier to scan. """ def filter(self, record): """ Add an emoji attribute to the log record. Args: record (LogRecord): The log record to filter Returns: bool: Always returns True to include the record """ record.emoji = LOG_EMOJIS.get(record.levelname, '') return True # Create console handler if enable_console: console_handler = logging.StreamHandler() console_handler.setLevel(level) console_formatter = logging.Formatter(log_format) console_handler.setFormatter(console_formatter) console_handler.addFilter(EmojiFilter()) logger.addHandler(console_handler) # Create file handler if enable_file: # Use provided log_file or generate daily log file file_path = log_file if log_file else get_daily_log_filename() file_handler = logging.FileHandler(file_path) file_handler.setLevel(level) file_formatter = logging.Formatter(log_format) file_handler.setFormatter(file_formatter) file_handler.addFilter(EmojiFilter()) logger.addHandler(file_handler) return logger def log_time(function_name, time_taken): """ Log the time taken by a function to a CSV file and to the logger. This function records performance metrics for functions, storing them in a CSV file for later analysis and also outputting them to the log with a timer emoji. Args: function_name (str): Name of the function being timed time_taken (float): Time taken in seconds Example: >>> log_time("process_video", 2.345) # Writes to CSV and logs: "⏱️ Function process_video took 2.3450 seconds" """ # Create file with headers if it doesn't exist if not os.path.exists(TIME_LOGS_FILE): with open(TIME_LOGS_FILE, 'w', newline='') as f: writer = csv.writer(f) writer.writerow(['timestamp', 'function', 'time_taken_seconds']) # Append time log with open(TIME_LOGS_FILE, 'a', newline='') as f: writer = csv.writer(f) writer.writerow([datetime.now().isoformat(), function_name, time_taken]) logger.info(f"{LOG_EMOJIS['TIMER']} Function {function_name} took {time_taken:.4f} seconds") def time_it(func): """ Decorator to measure and log the execution time of a function. This decorator wraps a function to measure its execution time and automatically log the results using the log_time function. It's a convenient way to add performance monitoring to any function. Args: func (callable): The function to decorate Returns: callable: A wrapped function that logs its execution time Example: >>> @time_it >>> def process_data(data): >>> # Process data here >>> return result >>> >>> # When called, will automatically log execution time >>> result = process_data(my_data) """ def wrapper(*args, **kwargs): """ Wrapper function that times the execution of the decorated function. Args: *args: Arguments to pass to the decorated function **kwargs: Keyword arguments to pass to the decorated function Returns: Any: The return value of the decorated function """ start_time = time.time() result = func(*args, **kwargs) end_time = time.time() time_taken = end_time - start_time log_time(func.__name__, time_taken) return result return wrapper def log_success(logger, message, *args, **kwargs): """ Log a success message with a checkmark emoji. This function provides a convenient way to log successful operations with a distinctive checkmark emoji, making success messages stand out in the logs. Args: logger (logging.Logger): Logger instance to use message (str): Message to log *args: Additional positional arguments for logger.info **kwargs: Additional keyword arguments for logger.info Example: >>> logger = setup_logger("my_module") >>> log_success(logger, "User registration completed for user_id={}", user_id) # Logs: "✅ User registration completed for user_id=123" """ logger.info(f"{LOG_EMOJIS['SUCCESS']} {message}", *args, **kwargs)