File size: 8,950 Bytes
8ae78b0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | 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) |