Source code for exosim.log.logger

"""
Modern Logger class using structlog as backend.

This module provides a modern structured logging implementation
for ExoSim 2.0 Task classes using structlog for enhanced capabilities.
"""

from .structlog_config import ExoSimLogger, get_default_logger, with_logger


[docs] class Logger: """ Modern structured logger for ExoSim 2.0 Tasks. This class uses structlog as the backend for structured logging capabilities, providing enhanced observability and better log analysis. """ def __init__(self): """Initialize the logger with automatic name detection.""" import contextlib with contextlib.suppress(TypeError): super().__init__() self.set_log_name()
[docs] def set_log_name(self) -> None: """ Set the logger name based on the class that inherits this Logger. Creates a structured logger with automatic class context detection. """ class_name = self.__class__.__name__ # Create structured logger with class context (no module to keep logs clean) self._logger = ExoSimLogger.for_class(self, class_name=class_name) # Store logger name self._log_name = f"exosim.{class_name}"
# Standard logging methods
[docs] def debug(self, message: str, *args, **kwargs) -> None: """ Log debug message. Parameters ---------- message : str Log message (supports % formatting) *args Positional arguments for message formatting **kwargs Additional structured data for the log record """ formatted_message = self._format_message(message, args) self._logger.debug(formatted_message, **kwargs)
[docs] def info(self, message: str, *args, **kwargs) -> None: """ Log info message. Parameters ---------- message : str Log message *args Positional arguments for message formatting **kwargs Additional structured data """ formatted_message = self._format_message(message, args) self._logger.info(formatted_message, **kwargs)
[docs] def warning(self, message: str, *args, **kwargs) -> None: """ Log warning message. Parameters ---------- message : str Log message *args Positional arguments for message formatting **kwargs Additional structured data """ formatted_message = self._format_message(message, args) self._logger.warning(formatted_message, **kwargs)
[docs] def error(self, message: str, *args, **kwargs) -> None: """ Log error message. Parameters ---------- message : str Log message *args Positional arguments for message formatting **kwargs Additional structured data """ formatted_message = self._format_message(message, args) self._logger.error(formatted_message, **kwargs)
[docs] def critical(self, message: str, *args, **kwargs) -> None: """ Log critical message. Parameters ---------- message : str Log message *args Positional arguments for message formatting **kwargs Additional structured data """ formatted_message = self._format_message(message, args) self._logger.critical(formatted_message, **kwargs)
# ExoSim-specific logging methods
[docs] def trace(self, message: str, *args, **kwargs) -> None: """ Log trace message. Maps to debug level with trace marker for structured logging. """ formatted_message = self._format_message(message, args) self._logger.trace(formatted_message, **kwargs)
[docs] def announce(self, message: str, *args, **kwargs) -> None: """ Log announcement message. Maps to info level with announcement marker. """ formatted_message = self._format_message(message, args) self._logger.announce(formatted_message, **kwargs)
[docs] def graphics(self, message: str, *args, **kwargs) -> None: """ Log graphics-related message. Maps to info level with graphics category marker. """ formatted_message = self._format_message(message, args) self._logger.graphics(formatted_message, **kwargs)
# Enhanced structured logging methods
[docs] def bind(self, **context) -> "Logger": """ Create a new logger with additional context bound to all messages. Parameters ---------- **context Key-value pairs to bind to the logger context Returns ------- Logger New logger instance with bound context Usage ----- task_logger = self.bind(task_id="sim_001", instrument="ARIEL") task_logger.info("Starting simulation") # Will include task_id and instrument """ new_logger = Logger.__new__(Logger) new_logger._logger = self._logger.bind(**context) new_logger._log_name = self._log_name return new_logger
[docs] def time_operation(self, operation_name: str, **context): """ Context manager for timing operations with structured logging. Parameters ---------- operation_name : str Name of the operation being timed **context Additional context to include in timing logs Usage ----- with self.time_operation("focal_plane_generation", channels=4): # ... focal plane generation logic ... pass """ return self._logger.time_operation(operation_name, **context)
[docs] def log_runtime(self, message: str, level: str = "info") -> None: """ Log runtime since last call. Parameters ---------- message : str Log message level : str Log level (debug, info, warning, error, critical) """ self._logger.log_runtime(message, level)
[docs] def log_runtime_complete(self, message: str, level: str = "info") -> None: """ Log total runtime since logger creation. Parameters ---------- message : str Log message level : str Log level """ self._logger.log_runtime_complete(message, level)
# Utility methods def _format_message(self, message: str, args: tuple) -> str: """ Format message with args using % formatting. Handles % formatting while gracefully handling formatting errors. """ if args: try: return message % args except (TypeError, ValueError): # If formatting fails, return the original message return message return message # Properties @property
[docs] def logger(self): """Access to the underlying structured logger.""" return self._logger
@property
[docs] def log_name(self) -> str: """Get the logger name.""" return self._log_name
[docs] def get_logger(name: str = "exosim"): """Get a logger instance.""" return get_default_logger()
__all__ = ["Logger", "get_logger", "with_logger"]