Spaces:
Paused
Paused
File size: 6,182 Bytes
a5784e9 | 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 | """
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
# =============================================================================
@contextmanager
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)
@contextmanager
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
|