from logging.config import ConvertingList, ConvertingDict, valid_ident from logging.handlers import QueueHandler, QueueListener from queue import Queue import atexit class QueueHnadlerHelper: @staticmethod def resolve_handlers(l): if not isinstance(l, ConvertingList): return l # Indexing the list performs the evaluation. return [l[i] for i in range(len(l))] @staticmethod def resolve_queue(q): if not isinstance(q, ConvertingDict): return q if '__resolved_value__' in q: return q['__resolved_value__'] cname = q.pop('class') klass = q.configurator.resolve(cname) props = q.pop('.', None) kwargs = {k: q[k] for k in q if valid_ident(k)} result = klass(**kwargs) if props: for name, value in props.items(): setattr(result, name, value) q['__resolved_value__'] = result return result # The guy from this video https://www.youtube.com/watch?v=9L77QExPmI0 is using logging features only available in 3.12 # This article had the class required to build the queue handler in 3.11 # https://rob-blackbourn.medium.com/how-to-use-python-logging-queuehandler-with-dictconfig-1e8b1284e27a class QueueListenerHandler(QueueHandler): def __init__(self, handlers, respect_handler_level=False, auto_run=True, queue=Queue(-1)): queue = QueueHnadlerHelper.resolve_queue(queue) super().__init__(queue) handlers = QueueHnadlerHelper.resolve_handlers(handlers) self._listener = QueueListener( self.queue, *handlers, respect_handler_level=respect_handler_level) if auto_run: self.start() atexit.register(self.stop) def start(self): self._listener.start() def stop(self): self._listener.stop() def emit(self, record): return super().emit(record)