Update utils/logger.py
Browse files- utils/logger.py +55 -10
utils/logger.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
"""
|
| 2 |
Logging Management Module
|
| 3 |
========================
|
|
@@ -28,7 +29,8 @@
|
|
| 28 |
import traceback
|
| 29 |
from functools import wraps
|
| 30 |
import time
|
| 31 |
-
from typing import Dict, List
|
|
|
|
| 32 |
|
| 33 |
class ColoredFormatter(logging.Formatter):
|
| 34 |
"""Custom formatter with colors for console output"""
|
|
@@ -50,6 +52,7 @@ def format(self, record):
|
|
| 50 |
|
| 51 |
return super().format(record)
|
| 52 |
|
|
|
|
| 53 |
class BackgroundFXLogger:
|
| 54 |
"""Main logger class for BackgroundFX Pro"""
|
| 55 |
|
|
@@ -82,6 +85,13 @@ def __init__(self,
|
|
| 82 |
# Performance tracking
|
| 83 |
self.performance_data = {}
|
| 84 |
self.start_times = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
| 86 |
def _setup_handlers(self):
|
| 87 |
"""Setup logging handlers for console and file output"""
|
|
@@ -106,7 +116,8 @@ def _setup_handlers(self):
|
|
| 106 |
file_handler = logging.handlers.RotatingFileHandler(
|
| 107 |
main_log_file,
|
| 108 |
maxBytes=10*1024*1024, # 10MB
|
| 109 |
-
backupCount=5
|
|
|
|
| 110 |
)
|
| 111 |
file_handler.setLevel(self.level)
|
| 112 |
|
|
@@ -123,7 +134,8 @@ def _setup_handlers(self):
|
|
| 123 |
error_handler = logging.handlers.RotatingFileHandler(
|
| 124 |
error_log_file,
|
| 125 |
maxBytes=5*1024*1024, # 5MB
|
| 126 |
-
backupCount=3
|
|
|
|
| 127 |
)
|
| 128 |
error_handler.setLevel(logging.ERROR)
|
| 129 |
error_handler.setFormatter(file_formatter)
|
|
@@ -247,8 +259,11 @@ def _save_performance_data(self):
|
|
| 247 |
# Load existing data
|
| 248 |
existing_data = {}
|
| 249 |
if self.performance_log_file.exists():
|
| 250 |
-
with open(self.performance_log_file, 'r') as f:
|
| 251 |
-
|
|
|
|
|
|
|
|
|
|
| 252 |
|
| 253 |
# Merge with new data
|
| 254 |
existing_data.update(self.performance_data)
|
|
@@ -260,7 +275,7 @@ def _save_performance_data(self):
|
|
| 260 |
existing_data = {k: existing_data[k] for k in keep_keys}
|
| 261 |
|
| 262 |
# Save updated data
|
| 263 |
-
with open(self.performance_log_file, 'w') as f:
|
| 264 |
json.dump(existing_data, f, indent=2)
|
| 265 |
|
| 266 |
except Exception as e:
|
|
@@ -271,7 +286,7 @@ def get_log_files(self) -> Dict[str, str]:
|
|
| 271 |
return {
|
| 272 |
'main_log': str(self.logs_dir / "backgroundfx.log"),
|
| 273 |
'error_log': str(self.logs_dir / "errors.log"),
|
| 274 |
-
'performance_log': str(self.
|
| 275 |
'logs_directory': str(self.logs_dir)
|
| 276 |
}
|
| 277 |
|
|
@@ -283,13 +298,13 @@ def get_recent_logs(self, lines: int = 50) -> Dict[str, List[str]]:
|
|
| 283 |
# Main log
|
| 284 |
main_log_file = self.logs_dir / "backgroundfx.log"
|
| 285 |
if main_log_file.exists():
|
| 286 |
-
with open(main_log_file, 'r') as f:
|
| 287 |
logs['main'] = f.readlines()[-lines:]
|
| 288 |
|
| 289 |
# Error log
|
| 290 |
error_log_file = self.logs_dir / "errors.log"
|
| 291 |
if error_log_file.exists():
|
| 292 |
-
with open(error_log_file, 'r') as f:
|
| 293 |
logs['errors'] = f.readlines()[-lines:]
|
| 294 |
|
| 295 |
except Exception as e:
|
|
@@ -297,9 +312,11 @@ def get_recent_logs(self, lines: int = 50) -> Dict[str, List[str]]:
|
|
| 297 |
|
| 298 |
return logs
|
| 299 |
|
|
|
|
| 300 |
# Global logger instance
|
| 301 |
_global_logger: Optional[BackgroundFXLogger] = None
|
| 302 |
|
|
|
|
| 303 |
def setup_logging(logs_dir: str = "LOGS",
|
| 304 |
level: int = logging.INFO,
|
| 305 |
console_output: bool = True,
|
|
@@ -318,6 +335,16 @@ def setup_logging(logs_dir: str = "LOGS",
|
|
| 318 |
|
| 319 |
return _global_logger
|
| 320 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 321 |
def get_logger(name: str = None) -> BackgroundFXLogger:
|
| 322 |
"""Get logger instance"""
|
| 323 |
if _global_logger is None:
|
|
@@ -336,6 +363,7 @@ def get_logger(name: str = None) -> BackgroundFXLogger:
|
|
| 336 |
|
| 337 |
return _global_logger
|
| 338 |
|
|
|
|
| 339 |
def log_function_call(logger: BackgroundFXLogger = None):
|
| 340 |
"""Decorator to log function calls with timing"""
|
| 341 |
if logger is None:
|
|
@@ -362,6 +390,7 @@ def wrapper(*args, **kwargs):
|
|
| 362 |
return wrapper
|
| 363 |
return decorator
|
| 364 |
|
|
|
|
| 365 |
def log_processing_pipeline():
|
| 366 |
"""Decorator for logging processing pipeline steps"""
|
| 367 |
logger = get_logger()
|
|
@@ -384,6 +413,7 @@ def wrapper(*args, **kwargs):
|
|
| 384 |
return wrapper
|
| 385 |
return decorator
|
| 386 |
|
|
|
|
| 387 |
# Convenience functions
|
| 388 |
def log_info(message: str, **kwargs):
|
| 389 |
"""Quick info logging"""
|
|
@@ -401,10 +431,25 @@ def log_debug(message: str, **kwargs):
|
|
| 401 |
"""Quick debug logging"""
|
| 402 |
get_logger().debug(message, **kwargs)
|
| 403 |
|
|
|
|
| 404 |
# Initialize logging on module import
|
| 405 |
if _global_logger is None:
|
| 406 |
try:
|
| 407 |
setup_logging()
|
| 408 |
log_info("✅ Logging system initialized")
|
| 409 |
except Exception as e:
|
| 410 |
-
print(f"⚠️ Failed to initialize logging: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
"""
|
| 3 |
Logging Management Module
|
| 4 |
========================
|
|
|
|
| 29 |
import traceback
|
| 30 |
from functools import wraps
|
| 31 |
import time
|
| 32 |
+
from typing import Dict, List
|
| 33 |
+
|
| 34 |
|
| 35 |
class ColoredFormatter(logging.Formatter):
|
| 36 |
"""Custom formatter with colors for console output"""
|
|
|
|
| 52 |
|
| 53 |
return super().format(record)
|
| 54 |
|
| 55 |
+
|
| 56 |
class BackgroundFXLogger:
|
| 57 |
"""Main logger class for BackgroundFX Pro"""
|
| 58 |
|
|
|
|
| 85 |
# Performance tracking
|
| 86 |
self.performance_data = {}
|
| 87 |
self.start_times = {}
|
| 88 |
+
|
| 89 |
+
def __getattr__(self, name):
|
| 90 |
+
"""
|
| 91 |
+
Delegate unknown attributes/methods to the underlying stdlib logger.
|
| 92 |
+
This makes BackgroundFXLogger behave like logging.Logger where needed.
|
| 93 |
+
"""
|
| 94 |
+
return getattr(self.logger, name)
|
| 95 |
|
| 96 |
def _setup_handlers(self):
|
| 97 |
"""Setup logging handlers for console and file output"""
|
|
|
|
| 116 |
file_handler = logging.handlers.RotatingFileHandler(
|
| 117 |
main_log_file,
|
| 118 |
maxBytes=10*1024*1024, # 10MB
|
| 119 |
+
backupCount=5,
|
| 120 |
+
encoding="utf-8"
|
| 121 |
)
|
| 122 |
file_handler.setLevel(self.level)
|
| 123 |
|
|
|
|
| 134 |
error_handler = logging.handlers.RotatingFileHandler(
|
| 135 |
error_log_file,
|
| 136 |
maxBytes=5*1024*1024, # 5MB
|
| 137 |
+
backupCount=3,
|
| 138 |
+
encoding="utf-8"
|
| 139 |
)
|
| 140 |
error_handler.setLevel(logging.ERROR)
|
| 141 |
error_handler.setFormatter(file_formatter)
|
|
|
|
| 259 |
# Load existing data
|
| 260 |
existing_data = {}
|
| 261 |
if self.performance_log_file.exists():
|
| 262 |
+
with open(self.performance_log_file, 'r', encoding="utf-8") as f:
|
| 263 |
+
try:
|
| 264 |
+
existing_data = json.load(f)
|
| 265 |
+
except json.JSONDecodeError:
|
| 266 |
+
existing_data = {}
|
| 267 |
|
| 268 |
# Merge with new data
|
| 269 |
existing_data.update(self.performance_data)
|
|
|
|
| 275 |
existing_data = {k: existing_data[k] for k in keep_keys}
|
| 276 |
|
| 277 |
# Save updated data
|
| 278 |
+
with open(self.performance_log_file, 'w', encoding="utf-8") as f:
|
| 279 |
json.dump(existing_data, f, indent=2)
|
| 280 |
|
| 281 |
except Exception as e:
|
|
|
|
| 286 |
return {
|
| 287 |
'main_log': str(self.logs_dir / "backgroundfx.log"),
|
| 288 |
'error_log': str(self.logs_dir / "errors.log"),
|
| 289 |
+
'performance_log': str(self.performance_log_file),
|
| 290 |
'logs_directory': str(self.logs_dir)
|
| 291 |
}
|
| 292 |
|
|
|
|
| 298 |
# Main log
|
| 299 |
main_log_file = self.logs_dir / "backgroundfx.log"
|
| 300 |
if main_log_file.exists():
|
| 301 |
+
with open(main_log_file, 'r', encoding="utf-8") as f:
|
| 302 |
logs['main'] = f.readlines()[-lines:]
|
| 303 |
|
| 304 |
# Error log
|
| 305 |
error_log_file = self.logs_dir / "errors.log"
|
| 306 |
if error_log_file.exists():
|
| 307 |
+
with open(error_log_file, 'r', encoding="utf-8") as f:
|
| 308 |
logs['errors'] = f.readlines()[-lines:]
|
| 309 |
|
| 310 |
except Exception as e:
|
|
|
|
| 312 |
|
| 313 |
return logs
|
| 314 |
|
| 315 |
+
|
| 316 |
# Global logger instance
|
| 317 |
_global_logger: Optional[BackgroundFXLogger] = None
|
| 318 |
|
| 319 |
+
|
| 320 |
def setup_logging(logs_dir: str = "LOGS",
|
| 321 |
level: int = logging.INFO,
|
| 322 |
console_output: bool = True,
|
|
|
|
| 335 |
|
| 336 |
return _global_logger
|
| 337 |
|
| 338 |
+
|
| 339 |
+
# --- Backward-compat alias for legacy imports ---
|
| 340 |
+
def setup_logger(*args, **kwargs):
|
| 341 |
+
"""
|
| 342 |
+
Alias for old code: `from utils.logger import setup_logger`.
|
| 343 |
+
Behaves the same as setup_logging and returns a BackgroundFXLogger.
|
| 344 |
+
"""
|
| 345 |
+
return setup_logging(*args, **kwargs)
|
| 346 |
+
|
| 347 |
+
|
| 348 |
def get_logger(name: str = None) -> BackgroundFXLogger:
|
| 349 |
"""Get logger instance"""
|
| 350 |
if _global_logger is None:
|
|
|
|
| 363 |
|
| 364 |
return _global_logger
|
| 365 |
|
| 366 |
+
|
| 367 |
def log_function_call(logger: BackgroundFXLogger = None):
|
| 368 |
"""Decorator to log function calls with timing"""
|
| 369 |
if logger is None:
|
|
|
|
| 390 |
return wrapper
|
| 391 |
return decorator
|
| 392 |
|
| 393 |
+
|
| 394 |
def log_processing_pipeline():
|
| 395 |
"""Decorator for logging processing pipeline steps"""
|
| 396 |
logger = get_logger()
|
|
|
|
| 413 |
return wrapper
|
| 414 |
return decorator
|
| 415 |
|
| 416 |
+
|
| 417 |
# Convenience functions
|
| 418 |
def log_info(message: str, **kwargs):
|
| 419 |
"""Quick info logging"""
|
|
|
|
| 431 |
"""Quick debug logging"""
|
| 432 |
get_logger().debug(message, **kwargs)
|
| 433 |
|
| 434 |
+
|
| 435 |
# Initialize logging on module import
|
| 436 |
if _global_logger is None:
|
| 437 |
try:
|
| 438 |
setup_logging()
|
| 439 |
log_info("✅ Logging system initialized")
|
| 440 |
except Exception as e:
|
| 441 |
+
print(f"⚠️ Failed to initialize logging: {e}")
|
| 442 |
+
|
| 443 |
+
|
| 444 |
+
__all__ = [
|
| 445 |
+
"BackgroundFXLogger",
|
| 446 |
+
"setup_logging",
|
| 447 |
+
"setup_logger", # alias for legacy code
|
| 448 |
+
"get_logger",
|
| 449 |
+
"log_function_call",
|
| 450 |
+
"log_processing_pipeline",
|
| 451 |
+
"log_info",
|
| 452 |
+
"log_error",
|
| 453 |
+
"log_warning",
|
| 454 |
+
"log_debug",
|
| 455 |
+
]
|