Spaces:
Paused
Paused
| """ | |
| Core Logger Module | |
| """ | |
| import logging | |
| import sys | |
| from contextlib import contextmanager | |
| from typing import Any, Generator, Optional | |
| from colorama import init as colorama_init | |
| from logging_utils.core.context import ( | |
| request_id_var, | |
| source_var, | |
| ) | |
| from logging_utils.core.rendering import ( | |
| GridFormatter, | |
| _burst_buffer, | |
| format_object, | |
| ) | |
| # ============================================================================= | |
| # Logging Filters | |
| # ============================================================================= | |
| class BrowserNoiseFilter(logging.Filter): | |
| """Filter out benign browser noise (AbortError, CORS, Google logging, SSL).""" | |
| # Patterns to filter out | |
| NOISE_PATTERNS = [ | |
| "AbortError: The operation was aborted", # Playwright navigation cancellation | |
| "Cross-Origin Request Blocked", # CORS errors (usually harmless) | |
| "play.google.com/log", # Google's internal logging endpoint | |
| "APPLICATION_DATA_AFTER_CLOSE_NOTIFY", # SSL shutdown warning (harmless) | |
| ] | |
| def filter(self, record: logging.LogRecord) -> bool: | |
| """Return False to drop the log record, True to keep it.""" | |
| message = record.getMessage() | |
| for pattern in self.NOISE_PATTERNS: | |
| if pattern in message: | |
| return False | |
| return True | |
| # Legacy alias for backwards compatibility | |
| AbortErrorFilter = BrowserNoiseFilter | |
| # ============================================================================= | |
| # Context Managers | |
| # ============================================================================= | |
| def log_context( | |
| name: str, | |
| logger: Optional[logging.Logger] = None, | |
| source: Optional[str] = None, | |
| silent: bool = False, | |
| ) -> Generator[None, None, None]: | |
| """ | |
| Context manager for source switching (simplified - no tree tracking). | |
| Usage: | |
| with log_context("Processing request", logger): | |
| logger.info("Step 1") | |
| With silent=True, no header is logged: | |
| with log_context("", logger, silent=True): | |
| logger.info("Some work") | |
| """ | |
| if logger is None: | |
| logger = logging.getLogger() | |
| # Handle optional source change | |
| source_token = None | |
| if source is not None: | |
| source_token = source_var.set(source) | |
| # Log the context entry (unless silent) | |
| if not silent and name: | |
| logger.info(name) | |
| try: | |
| yield | |
| finally: | |
| # Restore previous source | |
| if source_token is not None: | |
| source_var.reset(source_token) | |
| def request_context( | |
| request_id: str, source: str = "WORKR" | |
| ) -> Generator[None, None, None]: | |
| """ | |
| Context manager for request lifecycle. | |
| Sets request ID and source for all logs within the context. | |
| Usage: | |
| with request_context("akvdate", source="WORKR"): | |
| logger.info("Processing...") | |
| """ | |
| # Set context variables | |
| id_token = request_id_var.set(request_id) | |
| source_token = source_var.set(source) | |
| try: | |
| yield | |
| finally: | |
| # Reset context variables | |
| request_id_var.reset(id_token) | |
| source_var.reset(source_token) | |
| # ============================================================================= | |
| # Convenience Functions | |
| # ============================================================================= | |
| def set_source(source: str) -> None: | |
| """Set the source identifier for subsequent logs.""" | |
| source_var.set(source) | |
| def set_request_id(request_id: str) -> None: | |
| """Set the request ID for subsequent logs.""" | |
| request_id_var.set(request_id) | |
| def get_source() -> str: | |
| """Get the current source identifier.""" | |
| try: | |
| return source_var.get() | |
| except LookupError: | |
| return "SYS" | |
| def get_request_id() -> str: | |
| """Get the current request ID.""" | |
| try: | |
| return request_id_var.get() | |
| except LookupError: | |
| return " " | |
| def flush_burst_buffer() -> None: | |
| """Flush any remaining burst-suppressed messages.""" | |
| result = _burst_buffer.flush() | |
| if result: | |
| print(result) | |
| def log_object( | |
| logger: logging.Logger, obj: Any, label: str = "Data", level: int = logging.INFO | |
| ) -> None: | |
| """ | |
| Log an object with YAML-style formatting. | |
| Args: | |
| logger: Logger instance to use | |
| obj: Object to dump (dict, list, etc.) | |
| label: Label for the data block | |
| level: Logging level to use | |
| """ | |
| logger.log(level, f"{label}:") | |
| formatted = format_object(obj, indent=1) | |
| for line in formatted.split("\n"): | |
| if line.strip(): | |
| logger.log(level, line) | |
| # ============================================================================= | |
| # Logger Setup | |
| # ============================================================================= | |
| def setup_grid_logging( | |
| level: int = logging.DEBUG, | |
| show_tree: bool = True, | |
| colorize: bool = True, | |
| burst_suppression: bool = True, | |
| logger_name: Optional[str] = None, | |
| ) -> logging.Logger: | |
| """ | |
| Configure the logging system with grid formatting. | |
| Args: | |
| level: Logging level (default: DEBUG) | |
| show_tree: Whether to show tree structure (default: True) | |
| colorize: Whether to apply colors (default: True) | |
| burst_suppression: Whether to suppress duplicate messages (default: True) | |
| logger_name: Optional logger name (default: root logger) | |
| Returns: | |
| Configured logger instance | |
| """ | |
| # Initialize colorama | |
| colorama_init(autoreset=False) | |
| # Get logger | |
| if logger_name: | |
| logger = logging.getLogger(logger_name) | |
| else: | |
| logger = logging.getLogger() | |
| logger.setLevel(level) | |
| # Remove existing handlers to avoid duplicates | |
| for handler in logger.handlers[:]: | |
| logger.removeHandler(handler) | |
| # Create console handler | |
| console_handler = logging.StreamHandler(sys.stdout) | |
| console_handler.setLevel(level) | |
| # Apply grid formatter | |
| formatter = GridFormatter( | |
| show_tree=show_tree, colorize=colorize, burst_suppression=burst_suppression | |
| ) | |
| console_handler.setFormatter(formatter) | |
| logger.addHandler(console_handler) | |
| return logger | |