In 3.12, I’m adding a new feature to allow configuration of logging.handlers.QueueHandler and logging.handlers.QueueListener via logging.config.dictConfig(). Here’s the relevant documentation - comments are welcome here or on the PR.
Configuring QueueHandler and QueueListener
If you want to configure a QueueHandler, noting that this is normally used in conjunction with a QueueListener, you can configure both together. After the configuration, the QueueListener instance will be available as the listener attribute of the created handler, and that in turn will be available to you using logging.getHandlerByName() and passing the name you have used for the QueueHandler in your configuration. The dictionary schema for configuring the pair is shown in the example YAML snippet below.
If the queue key is present, the corresponding value can be one of the following:
An actual instance of queue.Queue or a subclass thereof. This is of course only possible if you are constructing or modifying the configuration dictionary in code.
A string that resolves to a callable which, when called with no arguments, returns the queue.Queue instance to use. That callable could be a queue.Queue subclass or a function which returns a suitable queue instance, such as my.module.queue_factory() .
A dict with a '()' key which is constructed in the usual way as discussed here in the documentation. The result of this construction should be a queue.Queue instance.
If the queue key is absent, a standard unbounded queue.Queue instance is created and used.
If the listener key is present, the corresponding value can be one of the following:
A subclass of logging.handlers.QueueListener. This is of course only possible if you are constructing or modifying the configuration dictionary in code.
A string which resolves to a class which is a subclass of QueueListener , such as 'my.package.CustomListener' .
A dict with a '()' key which is constructed in the usual way as discussed here in the documentation. The result of this construction should be a callable with the same signature as the QueueListener initializer.
If the listener key is absent, logging.handlers.QueueListener is used.
The values under the handlers key are the names of other handlers in the configuration (not shown in the above snippet) which will be passed to the queue listener.
Any custom queue handler and listener classes will need to be defined with the same initialization signatures as QueueHandler and QueueListener.
Is there a recommended way to setup a dict config for QueueHandlers and Listeners for python < 3.12?
I could setup a factory method which is pointed to for one of the handlers in the config which builds a QueueHandler, QueueListener, and queue that connects them and returns the QueueHandler so it can be connected to loggers, but how to connect handlers to the listener (without needing to resolve the config dicts into python objects myself in the factory method) or access the listener after configuration for start/stop?
Or is this just something that won’t be possible for python < 3.12 and I should give up and keep QueueHandler/Listener config in python code rather than a config file?
You could use the same basic scheme as the one I’ve added - just make it a custom handler factory (using the () key, as per the documentation) that does the configuration in code.
So the queue object and handler objects passed into the listener remain as their uninitialized config dictionary entries basically. Is this the expected behavior? Am I required to parse these dictionary config objects myself in QueueHandlerListener?
An arbitrary number of endpoint handlers can be configured for the queue hander listener in the config dict. The only slight funniness is allowing endpoint handlers to be passed into the QueueHandlerListener as eitherargs or kwargs but this isn’t even that bad.
I guess the point is that the config syntax doesn’t recurse down into input arguments. That is, the (), cfg:// and ext:// etc. syntaxes can be used to resolve inputs to formatters, handlers, and loggers, but it seems they can’t generically be nested to realize construction of input arguments.
How to setup only QueueHandler in a dictConfig(), and skip initiating QueueListener at all?
My usecase is that I have multiple worker processes (that logs to a QueueHandler) with dedicated background logging process (that consumes the queue).
Workers does not need QueueListeners to be configured, while logger process does not need QueueHandlers.
Now I am just create QueueHandler object, and not using this nice feature you have introduced… :-(