Source code for pytrnsys_process.log.logger

"""
Configures logging for the pytrnsys_process package with three outputs:
1. Console output (INFO level) - Shows basic messages without stacktrace
2. Regular log file (INFO level) - Logs to pytrnsys_process.log without stacktrace
3. Debug log file (DEBUG level) - Logs to pytrnsys_process_debug.log with full stacktrace

The logging setup includes custom formatting for each handler and uses a TracebackInfoFilter
to control stacktrace visibility in different outputs. The main logger is configured at DEBUG
level to capture all logging events, while individual handlers control what gets displayed
in each output.

All handlers use the same log record.
Once the log record is modified and anything removed from it, will not be available in the other handlers.
"""

import logging as _logging
import pathlib as _pl
import sys as _sys


class _TracebackInfoFilter(_logging.Filter):
    """Clear or restore the exception on log records
    Copied from, seems to be only solution that works
    https://stackoverflow.com/questions/54605699/python-logging-disable-stack-trace
    """

    # pylint: disable=protected-access

    def __init__(self, clear=True):  # pylint: disable=super-init-not-called
        self.clear = clear

    def filter(self, record):
        if self.clear:
            record._exc_info_hidden, record.exc_info = record.exc_info, None
            # clear the exception traceback text cache, if created.
            record.exc_text = None
        elif hasattr(record, "_exc_info_hidden"):
            record.exc_info = record._exc_info_hidden
            del record._exc_info_hidden
        return True


_console_format = _logging.Formatter("%(levelname)s - %(message)s")

# Default console logger, used as default logger in functions
default_console_logger = _logging.getLogger("default_pytrnsys_process")
_default_console_handler = _logging.StreamHandler(_sys.stdout)
_default_console_handler.setLevel(_logging.INFO)
default_console_logger.addHandler(_default_console_handler)


[docs] def get_main_logger(path: _pl.Path) -> _logging.Logger: main_logger = _logging.getLogger("main_logger") # Check if handlers already exist to avoid duplicates if main_logger.handlers: return main_logger console_handler = _logging.StreamHandler(_sys.stdout) console_handler.setLevel(_logging.INFO) # Regular log file without stacktrace file_handler = _logging.FileHandler( path / "pytrnsys_process.log", mode="a" ) file_handler.setLevel(_logging.INFO) # Debug log file with stacktrace debug_file_handler = _logging.FileHandler( path / "pytrnsys_process_debug.log", mode="a" ) debug_file_handler.setLevel(_logging.DEBUG) # configure formatters file_format = _logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) # set formatters console_handler.setFormatter(_console_format) file_handler.setFormatter(file_format) debug_file_handler.setFormatter(file_format) # add filters console_handler.addFilter(_TracebackInfoFilter()) file_handler.addFilter(_TracebackInfoFilter()) # Add this handler first because the other handlers will modify the log record main_logger.addHandler(debug_file_handler) main_logger.addHandler(console_handler) main_logger.addHandler(file_handler) main_logger.setLevel(_logging.DEBUG) return main_logger
[docs] def initialize_logs(path: _pl.Path): """Initialize log files by clearing their contents at the start of a new run.""" # Clear main log files by opening them in write mode briefly with open(path / "pytrnsys_process.log", "w", encoding="utf-8"): pass with open(path / "pytrnsys_process_debug.log", "w", encoding="utf-8"): pass
[docs] def get_simulation_logger(simulation_path: _pl.Path) -> _logging.Logger: """Create a logger specific to a simulation directory. Parameters __________ simulation_path: Path to the simulation directory Returns _______ Logger instance configured to write to a log file in the simulation directory """ sim_logger = _logging.getLogger( f"simulation_logger.{simulation_path.name}" ) # Check if handlers already exist to avoid duplicates if sim_logger.handlers: return sim_logger log_file = simulation_path / "processing.log" sim_file_handler = _logging.FileHandler(log_file, mode="w") sim_file_handler.setLevel(_logging.INFO) # Use same format as main logger but without name since it's simulation specific sim_format = _logging.Formatter( "%(asctime)s - %(levelname)s - %(message)s" ) sim_file_handler.setFormatter(sim_format) sim_logger.addHandler(sim_file_handler) sim_logger.setLevel(_logging.INFO) return sim_logger