"""Logging adapter for owsclient with automatic extra kwargs injection.""" from __future__ import annotations import logging from typing import Any, MutableMapping # Global dictionary to store default extra kwargs for all loggers _DEFAULT_EXTRA_KWARGS: dict[str, Any] = { "library": { "name": "python-owsclient", "language": "python", } } class OwsClientLoggerAdapter(logging.LoggerAdapter[logging.Logger]): """Logger adapter that automatically merges extra kwargs. This adapter combines: 1. Global default extras (set via set_default_extra_kwargs) 2. Instance-level extras (passed when creating the adapter) 3. Per-call extras (passed to individual log calls) Later extras override earlier ones in case of conflicts. """ def process( self, msg: str, kwargs: MutableMapping[str, Any] ) -> tuple[str, MutableMapping[str, Any]]: """Process the logging call, merging all extra kwargs. Args: ---- msg: The log message kwargs: Keyword arguments for the log call Returns: ------- Tuple of (message, updated kwargs) """ # Start with global defaults merged_extra = _DEFAULT_EXTRA_KWARGS.copy() # Merge instance-level extras if self.extra: merged_extra.update(self.extra) # Merge per-call extras if "extra" in kwargs: merged_extra.update(kwargs["extra"]) # Update kwargs with merged extras kwargs["extra"] = merged_extra return msg, kwargs def get_logger( name: str, extra: dict[str, Any] | None = None ) -> OwsClientLoggerAdapter: """Get a logger adapter with automatic extra kwargs injection. Args: ---- name: Logger name (typically __name__) extra: Optional instance-level extra kwargs to include in all logs Returns: ------- OwsClientLoggerAdapter instance Example: ------- >>> logger = get_logger(__name__) >>> logger.info("Something happened") # Will include default extras >>> >>> logger = get_logger(__name__, extra={"component": "auth"}) >>> logger.info("Auth event") # Will include defaults + component """ base_logger = logging.getLogger(name) return OwsClientLoggerAdapter(base_logger, extra or {})