Source code for ml_grid.util.logger_setup

import logging
import os
import sys
from datetime import datetime
from types import FrameType
from typing import Any, Optional


[docs] def setup_logger(log_folder_path: str = ".") -> logging.Logger: """Sets up a logger that writes to a file and the console. This function configures a logger to save debug-level messages to a timestamped log file and info-level messages to the console. It also sets up a system trace to log file and line number for debugging purposes, and redirects stdout to the logger. Args: log_folder_path (str, optional): The directory where log files will be stored. Defaults to ".". Returns: logging.Logger: The configured logger instance. """ # Get the root directory of the notebook notebook_dir = "" try: from IPython import get_ipython ipython_instance = get_ipython() if ipython_instance: notebook_dir = os.path.dirname( ipython_instance.config["IPKernelApp"]["connection_file"] ) except (ImportError, NameError, KeyError, AttributeError): # Fallback if IPython is not available or configured as expected notebook_dir = os.path.dirname(os.path.abspath(sys.argv[0] if sys.argv else __file__)) print("notebook_dir", notebook_dir) # Navigate up from the notebook directory to get the logs directory folder_name = log_folder_path current_dir = os.getcwd() # Combine the current directory and folder name to get the target directory logs_dir = os.path.abspath(os.path.join(current_dir, folder_name)) print("logs_dir", logs_dir) os.makedirs(logs_dir, exist_ok=True) # Get the current date and time current_date_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") # Set up logging with the current date and time in the log file name log_file = os.path.join(logs_dir, f"{current_date_time}_ml_grid.log") logging.basicConfig( filename=log_file, level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", ) # Create a logger logger = logging.getLogger(__name__) # logger.addFilter(ExcludeMatplotlibFontManagerFilter()) # Define a handler to print log messages to console console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(logging.INFO) formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) console_handler.setFormatter(formatter) logger.addHandler(console_handler) # Define a trace function for logging def tracefunc(frame: FrameType, event: str, arg: Any) -> Optional[callable]: """A trace function to log execution line by line for debugging. This function is registered with `sys.settrace` and logs the file and line number for each line event that occurs within the project's directory, ignoring certain library paths. Args: frame (FrameType): The current stack frame. event (str): The type of event (e.g., 'line', 'call'). arg (Any): Event-specific argument. Returns: Optional[callable]: The trace function itself to continue tracing, or None to stop tracing in the current scope. """ substrings_to_ignore = [ "matplotlib.font_manager", "matplotlib", ] # Only log events from files within the notebook directory if notebook_dir in frame.f_code.co_filename: filename = frame.f_code.co_filename if any(substring in filename for substring in substrings_to_ignore): return None if event == "line": logger.debug( f"{event}: {frame.f_code.co_filename} - Line {frame.f_lineno}" ) return tracefunc # Register the trace function globally for all events sys.settrace(tracefunc) # Redirect stdout to the logger class LoggerWriter: """A file-like object to redirect stdout to a logger.""" def __init__(self, logger: logging.Logger, level: int): """Initializes the LoggerWriter. Args: logger (logging.Logger): The logger instance to write to. level (int): The logging level to use (e.g., logging.INFO). """ self.logger = logger self.level = level def write(self, message: str) -> None: """Writes a message to the logger. Empty messages are ignored. Args: message (str): The message to log. """ if message.strip(): self.logger.log(self.level, message.strip()) def flush(self) -> None: """A no-op flush method to comply with the file-like object interface.""" pass sys.stdout = LoggerWriter(logger, logging.INFO) return logger