test / behavior_backend /app /utils /logging_utils.py
hibatorrahmen's picture
Add backend application and Dockerfile
8ae78b0
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)