File size: 5,796 Bytes
cdf55aa
906d397
 
cdf55aa
906d397
 
cdf55aa
906d397
cdf55aa
 
 
 
 
 
 
 
a8fa9c8
7fcecc9
 
 
 
a8fa9c8
cdf55aa
 
 
 
 
 
 
 
 
906d397
 
cdf55aa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
906d397
cdf55aa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
906d397
cdf55aa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
906d397
cdf55aa
 
 
 
 
 
 
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
# logging_config.py - Production-ready logging configuration

import logging
import logging.handlers
import sys
import os
from typing import Optional

def setup_logging(log_level: str = "INFO", log_dir: str = "logs") -> None:
    """
    Configures structured logging for the AI Lab with rotation and proper encoding.
    
    Args:
        log_level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
        log_dir: Directory for log files
    """
    try:
        os.makedirs(log_dir, exist_ok=True)
    except Exception as e:
        print(f"Error creating log directory: {e}")
        sys.exit(1)
        
    # Create formatters
    detailed_formatter = logging.Formatter(
        fmt="%(asctime)s [%(levelname)-8s] [%(name)-20s] %(funcName)-15s:%(lineno)-4d | %(message)s",
        datefmt="%Y-%m-%d %H:%M:%S"
    )
    
    simple_formatter = logging.Formatter(
        fmt="%(asctime)s [%(levelname)s] %(message)s",
        datefmt="%H:%M:%S"
    )
    
    # Create handlers
    handlers = []
    
    # Rotating file handler with UTF-8 encoding
    try:
        file_handler = logging.handlers.RotatingFileHandler(
            filename=os.path.join(log_dir, "ai_lab.log"),
            maxBytes=10 * 1024 * 1024,  # 10MB per file
            backupCount=5,  # Keep 5 backup files
            encoding='utf-8',
            mode='a'
        )
        file_handler.setFormatter(detailed_formatter)
        file_handler.setLevel(getattr(logging, log_level))
        handlers.append(file_handler)
    except Exception as e:
        print(f"Warning: Could not create file handler: {e}")
    
    # Console handler with UTF-8 support
    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setFormatter(simple_formatter)
    console_handler.setLevel(logging.INFO)  # Console shows INFO and above
    
    # Ensure UTF-8 encoding on Windows
    if hasattr(console_handler.stream, 'reconfigure'):
        console_handler.stream.reconfigure(encoding='utf-8')
    
    handlers.append(console_handler)
    
    # Configure root logger
    root_logger = logging.getLogger()
    root_logger.setLevel(getattr(logging, log_level))
    
    # Remove any existing handlers
    for handler in root_logger.handlers[:]:
        root_logger.removeHandler(handler)
    
    # Add new handlers
    for handler in handlers:
        root_logger.addHandler(handler)
    
    # Configure third-party library logging levels
    noisy_libraries = [
        "httpx",
        "urllib3",
        "sentence_transformers",
        "transformers",
        "faiss",
        "openai",
        "httpcore",
        "langchain"
    ]
    
    for lib in noisy_libraries:
        logging.getLogger(lib).setLevel(logging.WARNING)
    
    # Log startup message
    logging.info("="*60)
    logging.info("AI Lab Logging System Initialized")
    logging.info(f"Log Level: {log_level}")
    logging.info(f"Log Directory: {os.path.abspath(log_dir)}")
    logging.info("="*60)

def get_logger(name: str) -> logging.Logger:
    """
    Get a logger instance for a specific module.
    
    Args:
        name: Name of the module/component
        
    Returns:
        Configured logger instance
    """
    return logging.getLogger(name)

def log_exception(logger: logging.Logger, e: Exception, context: str = "") -> None:
    """
    Helper function to log exceptions with full traceback.
    
    Args:
        logger: Logger instance to use
        e: Exception to log
        context: Additional context about where the exception occurred
    """
    import traceback
    
    error_msg = f"Exception in {context}: {str(e)}" if context else str(e)
    logger.error(error_msg)
    logger.debug(f"Traceback:\n{traceback.format_exc()}")

def setup_performance_logging() -> Optional[logging.Logger]:
    """
    Set up a separate performance logger for tracking execution times and metrics.
    
    Returns:
        Performance logger instance
    """
    perf_logger = logging.getLogger("performance")
    perf_logger.setLevel(logging.DEBUG)
    
    # Create performance log file
    try:
        perf_handler = logging.handlers.RotatingFileHandler(
            filename=os.path.join("logs", "performance.log"),
            maxBytes=5 * 1024 * 1024,  # 5MB
            backupCount=3,
            encoding='utf-8'
        )
        perf_formatter = logging.Formatter(
            fmt="%(asctime)s.%(msecs)03d | %(message)s",
            datefmt="%Y-%m-%d %H:%M:%S"
        )
        perf_handler.setFormatter(perf_formatter)
        perf_logger.addHandler(perf_handler)
        
        return perf_logger
    except Exception as e:
        print(f"Warning: Could not create performance logger: {e}")
        return None

# Context manager for timed operations
class LoggedTimer:
    """Context manager for logging operation execution time."""
    
    def __init__(self, logger: logging.Logger, operation_name: str):
        self.logger = logger
        self.operation_name = operation_name
        self.start_time = None
    
    def __enter__(self):
        import time
        self.start_time = time.time()
        self.logger.debug(f"Starting: {self.operation_name}")
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        import time
        elapsed = time.time() - self.start_time
        if exc_type:
            self.logger.error(f"Failed: {self.operation_name} after {elapsed:.2f}s - {exc_val}")
        else:
            self.logger.debug(f"Completed: {self.operation_name} in {elapsed:.2f}s")

# Initialize logging when module is imported (can be overridden)
if __name__ != "__main__":
    # Auto-initialize with defaults when imported
    try:
        setup_logging()
    except Exception as e:
        print(f"Warning: Auto-initialization of logging failed: {e}")