|
21 | 21 | import os
|
22 | 22 | import re
|
23 | 23 | import warnings
|
| 24 | +import weakref |
24 | 25 | from enum import Enum
|
25 | 26 |
|
26 | 27 | import appdirs
|
@@ -116,49 +117,82 @@ def _configure_from_log_level(*extra_handlers: logging.Handler, log_level: int):
|
116 | 117 | # Get the root Morpheus logger
|
117 | 118 | morpheus_logger = logging.getLogger("morpheus")
|
118 | 119 |
|
119 |
| - # Set the level here |
120 |
| - set_log_level(log_level=log_level) |
| 120 | + # Prevent reconfiguration if called again |
| 121 | + if (not getattr(morpheus_logger, "_configured_by_morpheus", False)): |
| 122 | + setattr(morpheus_logger, "_configured_by_morpheus", True) |
121 | 123 |
|
122 |
| - # Dont propagate upstream |
123 |
| - morpheus_logger.propagate = False |
124 |
| - morpheus_logging_queue = multiprocessing.Queue() |
| 124 | + # Set the level here |
| 125 | + set_log_level(log_level=log_level) |
125 | 126 |
|
126 |
| - # This needs the be the only handler for morpheus logger |
127 |
| - morpheus_queue_handler = logging.handlers.QueueHandler(morpheus_logging_queue) |
| 127 | + # Dont propagate upstream |
| 128 | + morpheus_logger.propagate = False |
| 129 | + morpheus_logging_queue = multiprocessing.Queue() |
128 | 130 |
|
129 |
| - # At this point, any morpheus logger will propagate upstream to the morpheus root and then be handled by the queue |
130 |
| - # handler |
131 |
| - morpheus_logger.addHandler(morpheus_queue_handler) |
| 131 | + # This needs the be the only handler for morpheus logger |
| 132 | + morpheus_queue_handler = logging.handlers.QueueHandler(morpheus_logging_queue) |
132 | 133 |
|
133 |
| - log_file = os.path.join(appdirs.user_log_dir(appauthor="NVIDIA", appname="morpheus"), "morpheus.log") |
| 134 | + # At this point, any morpheus logger will propagate upstream to the morpheus root and then be handled by the |
| 135 | + # queue handler |
| 136 | + morpheus_logger.addHandler(morpheus_queue_handler) |
134 | 137 |
|
135 |
| - # Ensure the log directory exists |
136 |
| - os.makedirs(os.path.dirname(log_file), exist_ok=True) |
| 138 | + log_file = os.path.join(appdirs.user_log_dir(appauthor="NVIDIA", appname="morpheus"), "morpheus.log") |
137 | 139 |
|
138 |
| - # Now we build all of the handlers for the queue listener |
139 |
| - file_handler = logging.handlers.RotatingFileHandler(filename=log_file, backupCount=5, maxBytes=1000000) |
140 |
| - file_handler.setLevel(logging.DEBUG) |
141 |
| - file_handler.setFormatter( |
142 |
| - logging.Formatter('%(asctime)s - [%(levelname)s]: %(message)s {%(name)s, %(threadName)s}')) |
| 140 | + # Ensure the log directory exists |
| 141 | + os.makedirs(os.path.dirname(log_file), exist_ok=True) |
143 | 142 |
|
144 |
| - # Tqdm stream handler (avoids messing with progress bars) |
145 |
| - console_handler = TqdmLoggingHandler() |
| 143 | + # Now we build all of the handlers for the queue listener |
| 144 | + file_handler = logging.handlers.RotatingFileHandler(filename=log_file, backupCount=5, maxBytes=1000000) |
| 145 | + file_handler.setLevel(logging.DEBUG) |
| 146 | + file_handler.setFormatter( |
| 147 | + logging.Formatter('%(asctime)s - [%(levelname)s]: %(message)s {%(name)s, %(threadName)s}')) |
146 | 148 |
|
147 |
| - # Build and run the queue listener to actually process queued messages |
148 |
| - queue_listener = logging.handlers.QueueListener(morpheus_logging_queue, |
149 |
| - console_handler, |
150 |
| - file_handler, |
151 |
| - *extra_handlers, |
152 |
| - respect_handler_level=True) |
153 |
| - queue_listener.start() |
154 |
| - queue_listener._thread.name = "Logging Thread" |
| 149 | + # Tqdm stream handler (avoids messing with progress bars) |
| 150 | + console_handler = TqdmLoggingHandler() |
155 | 151 |
|
156 |
| - # Register a function to kill the listener thread before shutting down. prevents error on intpreter close |
157 |
| - def stop_queue_listener(): |
158 |
| - queue_listener.stop() |
| 152 | + # Build and run the queue listener to actually process queued messages |
| 153 | + queue_listener = logging.handlers.QueueListener(morpheus_logging_queue, |
| 154 | + console_handler, |
| 155 | + file_handler, |
| 156 | + *extra_handlers, |
| 157 | + respect_handler_level=True) |
| 158 | + queue_listener.start() |
| 159 | + queue_listener._thread.name = "Logging Thread" |
159 | 160 |
|
160 |
| - import atexit |
161 |
| - atexit.register(stop_queue_listener) |
| 161 | + # Register a function to kill the listener thread when the queue_handler is removed. |
| 162 | + weakref.finalize(morpheus_queue_handler, queue_listener.stop) |
| 163 | + |
| 164 | + # Register a handler before shutting down to remove all log handlers, this ensures that the weakref.finalize |
| 165 | + # handler we just defined is called at exit. |
| 166 | + import atexit |
| 167 | + atexit.register(reset_logging) |
| 168 | + else: |
| 169 | + raise RuntimeError("Logging has already been configured. Use `set_log_level` to change the log level or reset " |
| 170 | + "the logging system by calling `reset_logging`.") |
| 171 | + |
| 172 | + |
| 173 | +def reset_logging(logger_name: str = "morpheus"): |
| 174 | + """ |
| 175 | + Resets the Morpheus logging system. This will remove all handlers from the Morpheus logger and stop the queue |
| 176 | + listener. This is useful for testing where the logging system needs to be reconfigured multiple times or |
| 177 | + reconfigured with different settings. |
| 178 | + """ |
| 179 | + |
| 180 | + morpheus_logger = logging.getLogger(logger_name) |
| 181 | + |
| 182 | + for handler in morpheus_logger.handlers.copy(): |
| 183 | + # Copied from `logging.shutdown`. |
| 184 | + try: |
| 185 | + handler.acquire() |
| 186 | + handler.flush() |
| 187 | + handler.close() |
| 188 | + except (OSError, ValueError): |
| 189 | + pass |
| 190 | + finally: |
| 191 | + handler.release() |
| 192 | + morpheus_logger.removeHandler(handler) |
| 193 | + |
| 194 | + if hasattr(morpheus_logger, "_configured_by_morpheus"): |
| 195 | + delattr(morpheus_logger, "_configured_by_morpheus") |
162 | 196 |
|
163 | 197 |
|
164 | 198 | def configure_logging(*extra_handlers: logging.Handler, log_level: int = None, log_config_file: str = None):
|
|
0 commit comments