Skip to content

Utility Modules

Logger is a minimal, extensible logging utility with real-time broadcasting support.

It supports: - INFO, WARN, ERROR, DEBUG log levels - Custom listeners per instance or globally - Timestamped log dispatching without printing directly to stdout - Debug filtering controlled by the constructor flag

Listeners can be used to pipe logs to GUIs, files, or consoles with color formatting.

Source code in utils/logger.py
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
class Logger:
    """
    Logger is a minimal, extensible logging utility with real-time broadcasting support.

    It supports:
    - INFO, WARN, ERROR, DEBUG log levels
    - Custom listeners per instance or globally
    - Timestamped log dispatching without printing directly to stdout
    - Debug filtering controlled by the constructor flag

    Listeners can be used to pipe logs to GUIs, files, or consoles with color formatting.
    """

    _global_listeners = []  # Shared across all Logger instances

    def __init__(self, name='ORS', enable_debug=False):
        """
        Initialize a Logger instance.

        Args:
            name (str): Logical name for the logger (e.g., module or plugin name).
            enable_debug (bool): Whether DEBUG messages should be emitted.
        """
        self.name = name
        self.enable_debug = enable_debug
        self.listeners = list(Logger._global_listeners)  # Copy current global listeners

    @classmethod
    def add_global_listener(cls, listener_func):
        """
        Registers a listener that receives logs from *all* Logger instances, current and future.

        Args:
            listener_func (function): Callable with signature (level, name, message, timestamp).
        """
        cls._global_listeners.append(listener_func)

    def add_listener(self, listener_func):
        """
        Adds a listener for this specific logger instance.

        Args:
            listener_func (function): Callable with signature (level, name, message, timestamp).
        """
        self.listeners.append(listener_func)

    def remove_listener(self, listener_func):
        """
        Removes a previously added listener from this logger instance.

        Args:
            listener_func (function): The listener function to remove.
        """
        if listener_func in self.listeners:
            self.listeners.remove(listener_func)

    def _notify(self, level, message, timestamp):
        """
        Internal method to dispatch log messages to all registered listeners.

        Args:
            level (str): Log level (INFO, WARN, ERROR, DEBUG).
            message (str): The message to emit.
            timestamp (str): Formatted time string (e.g., "12:34:56").
        """
        for listener in self.listeners:
            try:
                listener(level, self.name, message, timestamp)
            except Exception as e:
                print(f"[Logger:{self.name}] Listener error: {e}", file=sys.stderr)

        #Case especially for GUI app :  notify global listeners directly (even if added after this logger was created)
        for global_listener in Logger._global_listeners:
            if global_listener not in self.listeners:
                try:
                    global_listener(level, self.name, message, timestamp)
                except Exception as e:
                    print(f"[Logger:{self.name}] Global listener error: {e}", file=sys.stderr)

    def _log(self, level, message):
        """
        Internal method to process a log event and notify listeners if allowed by debug filter.

        Args:
            level (str): Log level.
            message (str): Log message content.
        """
        if level == 'DEBUG' and not self.enable_debug:
            return

        now = datetime.datetime.now().strftime('%H:%M:%S')
        #print(message) #enable this to debug logging issues(?)
        self._notify(level, message, now)

    def info(self, message):
        """Log an INFO-level message."""
        self._log('INFO', message)

    def warn(self, message):
        """Log a WARN-level message."""
        self._log('WARN', message)

    def error(self, message):
        """Log an ERROR-level message."""
        self._log('ERROR', message)

    def debug(self, message):
        """Log a DEBUG-level message (only if enabled)."""
        self._log('DEBUG', message)

__init__(name='ORS', enable_debug=False)

Initialize a Logger instance.

Parameters:

Name Type Description Default
name str

Logical name for the logger (e.g., module or plugin name).

'ORS'
enable_debug bool

Whether DEBUG messages should be emitted.

False
Source code in utils/logger.py
42
43
44
45
46
47
48
49
50
51
52
def __init__(self, name='ORS', enable_debug=False):
    """
    Initialize a Logger instance.

    Args:
        name (str): Logical name for the logger (e.g., module or plugin name).
        enable_debug (bool): Whether DEBUG messages should be emitted.
    """
    self.name = name
    self.enable_debug = enable_debug
    self.listeners = list(Logger._global_listeners)  # Copy current global listeners

add_global_listener(listener_func) classmethod

Registers a listener that receives logs from all Logger instances, current and future.

Parameters:

Name Type Description Default
listener_func function

Callable with signature (level, name, message, timestamp).

required
Source code in utils/logger.py
54
55
56
57
58
59
60
61
62
@classmethod
def add_global_listener(cls, listener_func):
    """
    Registers a listener that receives logs from *all* Logger instances, current and future.

    Args:
        listener_func (function): Callable with signature (level, name, message, timestamp).
    """
    cls._global_listeners.append(listener_func)

add_listener(listener_func)

Adds a listener for this specific logger instance.

Parameters:

Name Type Description Default
listener_func function

Callable with signature (level, name, message, timestamp).

required
Source code in utils/logger.py
64
65
66
67
68
69
70
71
def add_listener(self, listener_func):
    """
    Adds a listener for this specific logger instance.

    Args:
        listener_func (function): Callable with signature (level, name, message, timestamp).
    """
    self.listeners.append(listener_func)

debug(message)

Log a DEBUG-level message (only if enabled).

Source code in utils/logger.py
133
134
135
def debug(self, message):
    """Log a DEBUG-level message (only if enabled)."""
    self._log('DEBUG', message)

error(message)

Log an ERROR-level message.

Source code in utils/logger.py
129
130
131
def error(self, message):
    """Log an ERROR-level message."""
    self._log('ERROR', message)

info(message)

Log an INFO-level message.

Source code in utils/logger.py
121
122
123
def info(self, message):
    """Log an INFO-level message."""
    self._log('INFO', message)

remove_listener(listener_func)

Removes a previously added listener from this logger instance.

Parameters:

Name Type Description Default
listener_func function

The listener function to remove.

required
Source code in utils/logger.py
73
74
75
76
77
78
79
80
81
def remove_listener(self, listener_func):
    """
    Removes a previously added listener from this logger instance.

    Args:
        listener_func (function): The listener function to remove.
    """
    if listener_func in self.listeners:
        self.listeners.remove(listener_func)

warn(message)

Log a WARN-level message.

Source code in utils/logger.py
125
126
127
def warn(self, message):
    """Log a WARN-level message."""
    self._log('WARN', message)