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)