File size: 2,486 Bytes
bef5e76 | 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 | """Structured logging middleware with structlog."""
import structlog
from functools import wraps
from typing import Callable, Any
import time
def configure_logging():
"""Configure structured logging."""
structlog.configure(
processors=[
structlog.stdlib.filter_by_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
structlog.processors.JSONRenderer()
],
context_class=dict,
logger_factory=structlog.stdlib.LoggerFactory(),
cache_logger_on_first_use=True,
)
def get_logger(name: str) -> structlog.stdlib.BoundLogger:
"""Get a configured logger."""
return structlog.get_logger(name)
def log_execution(logger: structlog.stdlib.BoundLogger):
"""Decorator to log function execution."""
def decorator(func: Callable) -> Callable:
@wraps(func)
async def async_wrapper(*args, **kwargs) -> Any:
start_time = time.time()
logger.info(f"Starting {func.__name__}")
try:
result = await func(*args, **kwargs)
duration = time.time() - start_time
logger.info(f"Completed {func.__name__}", duration=duration)
return result
except Exception as e:
duration = time.time() - start_time
logger.error(f"Error in {func.__name__}", error=str(e), duration=duration)
raise
@wraps(func)
def sync_wrapper(*args, **kwargs) -> Any:
start_time = time.time()
logger.info(f"Starting {func.__name__}")
try:
result = func(*args, **kwargs)
duration = time.time() - start_time
logger.info(f"Completed {func.__name__}", duration=duration)
return result
except Exception as e:
duration = time.time() - start_time
logger.error(f"Error in {func.__name__}", error=str(e), duration=duration)
raise
return async_wrapper if hasattr(func, '__call__') and hasattr(func, '__code__') and func.__code__.co_flags & 0x80 else sync_wrapper
return decorator
|