"""Logging configuration for TSU-WAVE""" import logging import sys import json from pathlib import Path from datetime import datetime from typing import Optional, Dict, Any import traceback class JsonFormatter(logging.Formatter): """JSON formatter for structured logging""" def format(self, record): log_record = { 'timestamp': datetime.utcnow().isoformat(), 'level': record.levelname, 'name': record.name, 'message': record.getMessage(), 'module': record.module, 'line': record.lineno } if hasattr(record, 'extra'): log_record.update(record.extra) if record.exc_info: log_record['exception'] = { 'type': record.exc_info[0].__name__, 'message': str(record.exc_info[1]), 'traceback': traceback.format_exception(*record.exc_info) } return json.dumps(log_record) def setup_logging( name: str = 'tsu-wave', log_file: Optional[str] = None, log_level: str = 'INFO', json_format: bool = False ) -> logging.Logger: """Setup logging configuration""" logger = logging.getLogger(name) logger.setLevel(getattr(logging, log_level.upper())) # Remove existing handlers logger.handlers.clear() # Console handler console_handler = logging.StreamHandler(sys.stdout) if json_format: console_handler.setFormatter(JsonFormatter()) else: console_handler.setFormatter( logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) ) logger.addHandler(console_handler) # File handler (if specified) if log_file: log_path = Path(log_file) log_path.parent.mkdir(parents=True, exist_ok=True) file_handler = logging.FileHandler(log_file) if json_format: file_handler.setFormatter(JsonFormatter()) else: file_handler.setFormatter( logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) ) logger.addHandler(file_handler) return logger def get_logger(name: str) -> logging.Logger: """Get logger instance""" return logging.getLogger(name) class LoggerAdapter(logging.LoggerAdapter): """Logger adapter with extra context""" def __init__(self, logger, extra=None): super().__init__(logger, extra or {}) def process(self, msg, kwargs): kwargs['extra'] = self.extra return msg, kwargs def with_context(self, **context): """Create new adapter with additional context""" new_extra = self.extra.copy() new_extra.update(context) return LoggerAdapter(self.logger, new_extra) def log_function_call(logger=None): """Decorator to log function calls""" def decorator(func): def wrapper(*args, **kwargs): log = logger or logging.getLogger(func.__module__) # Log function call log.debug(f"Calling {func.__name__}") try: result = func(*args, **kwargs) log.debug(f"{func.__name__} completed successfully") return result except Exception as e: log.error(f"{func.__name__} failed: {e}", exc_info=True) raise return wrapper return decorator def log_execution_time(logger=None): """Decorator to log function execution time""" def decorator(func): def wrapper(*args, **kwargs): log = logger or logging.getLogger(func.__module__) import time start = time.time() try: result = func(*args, **kwargs) elapsed = time.time() - start log.debug(f"{func.__name__} took {elapsed:.3f}s") return result except Exception as e: elapsed = time.time() - start log.error(f"{func.__name__} failed after {elapsed:.3f}s: {e}") raise return wrapper return decorator class PerformanceLogger: """Context manager for performance logging""" def __init__(self, name: str, logger=None): self.name = name self.logger = logger or logging.getLogger(__name__) self.start_time = None def __enter__(self): import time self.start_time = time.time() 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"{self.name} failed after {elapsed:.3f}s: {exc_val}") else: self.logger.debug(f"{self.name} completed in {elapsed:.3f}s") def setup_module_logger(module_name: str, log_level: str = 'INFO'): """Setup logger for a module""" logger = logging.getLogger(module_name) logger.setLevel(getattr(logging, log_level.upper())) # Only add handler if none exists if not logger.handlers: handler = logging.StreamHandler(sys.stdout) handler.setFormatter( logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) ) logger.addHandler(handler) return logger