| 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 |
| """ |
|
|
| |
| logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') |
| logger = logging.getLogger(__name__) |
|
|
| |
| logs_dir = Path("logs") |
| logs_dir.mkdir(exist_ok=True) |
|
|
| |
| TIME_LOGS_FILE = logs_dir / "time_logs.csv" |
|
|
| |
| 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) |
| |
| |
| for handler in logger.handlers[:]: |
| logger.removeHandler(handler) |
| |
| |
| log_format = '%(asctime)s - %(name)s - %(emoji)s %(levelname)s - %(message)s' |
| |
| |
| 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 |
| |
| |
| 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) |
| |
| |
| if enable_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" |
| """ |
| |
| 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']) |
| |
| |
| 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) |