The GlobalLogger class provides a flexible logging system for the SupplyNetPy simulations. It allows logging messages to both the console and a file, and can be easily enabled or disabled during runtime.

SupplyNetPy.Components.logger

GlobalLogger

GlobalLogger(logger_name=_LIBRARY_LOGGER_NAME, log_to_file=False, log_file='simulation_trace.log', log_to_screen=False)

Bases: LoggerAdapter

Library-wide logging.LoggerAdapter with on/off file and screen handlers.

Subclasses logging.LoggerAdapter so callers can write node.logger.info(...) (and debug / warning / error / critical) directly. (Older code had to write node.logger.logger.info(...) with two .logger steps; that is no longer needed.) The underlying logging.Logger is still reachable as self.logger (the standard adapter attribute), so older code that goes through that attribute keeps working.

The wrapper does nothing by default. GlobalLogger() attaches only a logging.NullHandler and does not open any file — output is opt-in. Call enable_logging (or pass log_to_file=True / log_to_screen=True to the constructor) to attach real handlers. This is the documented Python-library logging pattern; see https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library

The library exports a module-level global_logger = GlobalLogger() from core.py whose underlying logging.Logger is named "sim_trace". Every per-node logger created by Node lives at "sim_trace.<node.ID>" and propagates up to "sim_trace" — so all log routing is configured in one place by calling scm.global_logger.enable_logging(...). simulate_sc_net(logging=True) does this automatically; users who drive env.run(...) themselves and want a log file must call it explicitly.

Parameters:
  • logger_name (str, default: _LIBRARY_LOGGER_NAME ) –

    Name of the underlying logging.Logger. Defaults to "sim_trace".

  • log_to_file (bool, default: False ) –

    Attach a logging.FileHandler writing to log_file (mode 'w'). Defaults to False — opt-in.

  • log_file (str, default: 'simulation_trace.log' ) –

    Destination file path. Only opened when log_to_file is True. Defaults to "simulation_trace.log".

  • log_to_screen (bool, default: False ) –

    Attach a logging.StreamHandler. Defaults to False — opt-in.

Attributes:
  • logger (Logger) –

    The underlying logger instance (inherited from logging.LoggerAdapter).

  • log_to_file (bool) –

    Current file-handler flag.

  • log_to_screen (bool) –

    Current screen-handler flag.

  • log_file (str) –

    Current destination file path.

Methods:

Name Description
debug/info/warning/error/critical/log/exception

Inherited from logging.LoggerAdapter — call them directly on the adapter (e.g. logger.info(...)) instead of going through .logger.

set_log_file

Set the destination file and re-attach handlers.

get_log_file

Return the current log file path.

set_logger

Rebind self.logger to a different name.

configure_logger

Re-build handlers from current flags. Always attaches logging.NullHandler.

enable_logging

Opt-in: attach handlers and unmute the logger.

disable_logging

Mute the logger and drop handlers (NullHandler stays).

Source code in src/SupplyNetPy/Components/logger.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def __init__(self,
             logger_name=_LIBRARY_LOGGER_NAME,
             log_to_file=False,
             log_file='simulation_trace.log',
             log_to_screen=False):
    logger = logging.getLogger(logger_name)
    logger.setLevel(logging.DEBUG)
    # Stop this main logger from passing its messages further up to
    # Python's own root logger — otherwise our log lines would also land in
    # whatever logging the host application has set up. (Per-node loggers
    # still pass their messages up to this one; only this top logger stops
    # here.)
    if logger_name == _LIBRARY_LOGGER_NAME:
        logger.propagate = False
    # Start clean: an earlier GlobalLogger may have muted this same
    # underlying Python logger by setting disabled=True.
    logger.disabled = False
    # extra=None: we add no extra fields to each message, so the base
    # LoggerAdapter passes the message through unchanged.
    super().__init__(logger, extra=None)

    self.log_to_file = log_to_file
    self.log_to_screen = log_to_screen
    self.log_file = log_file
    self.configure_logger()

set_log_file

set_log_file(filename)

Set the destination log file and re-attach handlers.

Source code in src/SupplyNetPy/Components/logger.py
120
121
122
123
def set_log_file(self, filename):
    """Set the destination log file and re-attach handlers."""
    self.log_file = filename
    self.configure_logger()

get_log_file

get_log_file()

Return the current log file path.

Source code in src/SupplyNetPy/Components/logger.py
125
126
127
def get_log_file(self):
    """Return the current log file path."""
    return self.log_file

set_logger

set_logger(logger_name)

Rebind the underlying logger to a different name.

Source code in src/SupplyNetPy/Components/logger.py
129
130
131
def set_logger(self, logger_name):
    """Rebind the underlying logger to a different name."""
    self.logger = logging.getLogger(logger_name)

configure_logger

configure_logger()

Replace this logger's handlers based on current log_to_file / log_to_screen flags. A logging.NullHandler is always attached so the logger never triggers Python's "No handlers could be found" stderr fallback — the recommended default for library loggers.

Source code in src/SupplyNetPy/Components/logger.py
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
def configure_logger(self):
    """
    Replace this logger's handlers based on current ``log_to_file`` /
    ``log_to_screen`` flags. A `logging.NullHandler` is always
    attached so the logger never triggers Python's "No handlers could be
    found" stderr fallback — the recommended default for library loggers.
    """
    self._replace_handlers([logging.NullHandler()])

    if self.log_to_screen:
        screen_handler = logging.StreamHandler()
        screen_handler.setLevel(logging.DEBUG)
        screen_handler.setFormatter(logging.Formatter(_LOG_FORMAT))
        screen_handler.addFilter(_ShortNameFilter())
        self.logger.addHandler(screen_handler)

    if self.log_to_file:
        file_handler = logging.FileHandler(self.log_file, mode='w')
        file_handler.setLevel(logging.DEBUG)
        file_handler.setFormatter(logging.Formatter(_LOG_FORMAT))
        file_handler.addFilter(_ShortNameFilter())
        self.logger.addHandler(file_handler)

enable_logging

enable_logging(log_to_file=True, log_to_screen=True)

Opt-in. Unmute the logger and (re)attach screen and/or file handlers according to the arguments. Defaults are True for both so a bare enable_logging() call gives the user the historical "noisy" behavior on demand.

Source code in src/SupplyNetPy/Components/logger.py
156
157
158
159
160
161
162
163
164
def enable_logging(self, log_to_file=True, log_to_screen=True):
    """Opt-in. Unmute the logger and (re)attach screen and/or file handlers
    according to the arguments. Defaults are ``True`` for both so a bare
    ``enable_logging()`` call gives the user the historical "noisy"
    behavior on demand."""
    self.logger.disabled = False
    self.log_to_file = log_to_file
    self.log_to_screen = log_to_screen
    self.configure_logger()

disable_logging

disable_logging()

Mute the logger. Sets self.logger.disabled = True and drops all handlers (a logging.NullHandler is re-attached so the logger remains well-formed). Note: this only affects self.logger — it does not touch the global logging.disable switch, which would suppress the host application's own logging.

Source code in src/SupplyNetPy/Components/logger.py
166
167
168
169
170
171
172
173
174
175
def disable_logging(self):
    """Mute the logger. Sets ``self.logger.disabled = True`` and drops all
    handlers (a `logging.NullHandler` is re-attached so the logger
    remains well-formed). Note: this only affects ``self.logger`` — it
    does not touch the global ``logging.disable`` switch, which would
    suppress the host application's own logging."""
    self.logger.disabled = True
    self._replace_handlers([logging.NullHandler()])
    self.log_to_file = False
    self.log_to_screen = False