summaryrefslogtreecommitdiffstats
path: root/Lib/logging/config.py
diff options
context:
space:
mode:
authorVinay Sajip <vinay_sajip@yahoo.co.uk>2022-06-07 08:20:35 (GMT)
committerGitHub <noreply@github.com>2022-06-07 08:20:35 (GMT)
commit1b7480399162b5b469bb9533f5ceda53d16f6586 (patch)
treee3b6e276f5b7c33aa4ba46e57341b302cb123da9 /Lib/logging/config.py
parentc6f6ede728df144c6c39858f558ea65a2aac7cda (diff)
downloadcpython-1b7480399162b5b469bb9533f5ceda53d16f6586.zip
cpython-1b7480399162b5b469bb9533f5ceda53d16f6586.tar.gz
cpython-1b7480399162b5b469bb9533f5ceda53d16f6586.tar.bz2
gh-93162: Add ability to configure QueueHandler/QueueListener together (GH-93269)
Also, provide getHandlerByName() and getHandlerNames() APIs. Closes #93162.
Diffstat (limited to 'Lib/logging/config.py')
-rw-r--r--Lib/logging/config.py92
1 files changed, 83 insertions, 9 deletions
diff --git a/Lib/logging/config.py b/Lib/logging/config.py
index 86a1e4e..2b9d90c 100644
--- a/Lib/logging/config.py
+++ b/Lib/logging/config.py
@@ -1,4 +1,4 @@
-# Copyright 2001-2019 by Vinay Sajip. All Rights Reserved.
+# Copyright 2001-2022 by Vinay Sajip. All Rights Reserved.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose and without fee is hereby granted,
@@ -19,15 +19,17 @@ Configuration functions for the logging package for Python. The core package
is based on PEP 282 and comments thereto in comp.lang.python, and influenced
by Apache's log4j system.
-Copyright (C) 2001-2019 Vinay Sajip. All Rights Reserved.
+Copyright (C) 2001-2022 Vinay Sajip. All Rights Reserved.
To use, simply 'import logging' and log away!
"""
import errno
+import functools
import io
import logging
import logging.handlers
+import queue
import re
import struct
import threading
@@ -563,7 +565,7 @@ class DictConfigurator(BaseConfigurator):
handler.name = name
handlers[name] = handler
except Exception as e:
- if 'target not configured yet' in str(e.__cause__):
+ if ' not configured yet' in str(e.__cause__):
deferred.append(name)
else:
raise ValueError('Unable to configure handler '
@@ -702,6 +704,21 @@ class DictConfigurator(BaseConfigurator):
except Exception as e:
raise ValueError('Unable to add filter %r' % f) from e
+ def _configure_queue_handler(self, klass, **kwargs):
+ if 'queue' in kwargs:
+ q = kwargs['queue']
+ else:
+ q = queue.Queue() # unbounded
+ rhl = kwargs.get('respect_handler_level', False)
+ if 'listener' in kwargs:
+ lklass = kwargs['listener']
+ else:
+ lklass = logging.handlers.QueueListener
+ listener = lklass(q, *kwargs['handlers'], respect_handler_level=rhl)
+ handler = klass(q)
+ handler.listener = listener
+ return handler
+
def configure_handler(self, config):
"""Configure a handler from a dictionary."""
config_copy = dict(config) # for restoring in case of error
@@ -721,26 +738,83 @@ class DictConfigurator(BaseConfigurator):
factory = c
else:
cname = config.pop('class')
- klass = self.resolve(cname)
- #Special case for handler which refers to another handler
+ if callable(cname):
+ klass = cname
+ else:
+ klass = self.resolve(cname)
if issubclass(klass, logging.handlers.MemoryHandler) and\
'target' in config:
+ # Special case for handler which refers to another handler
try:
- th = self.config['handlers'][config['target']]
+ tn = config['target']
+ th = self.config['handlers'][tn]
if not isinstance(th, logging.Handler):
config.update(config_copy) # restore for deferred cfg
raise TypeError('target not configured yet')
config['target'] = th
except Exception as e:
- raise ValueError('Unable to set target handler '
- '%r' % config['target']) from e
+ raise ValueError('Unable to set target handler %r' % tn) from e
+ elif issubclass(klass, logging.handlers.QueueHandler):
+ # Another special case for handler which refers to other handlers
+ if 'handlers' not in config:
+ raise ValueError('No handlers specified for a QueueHandler')
+ if 'queue' in config:
+ qspec = config['queue']
+ if not isinstance(qspec, queue.Queue):
+ if isinstance(qspec, str):
+ q = self.resolve(qspec)
+ if not callable(q):
+ raise TypeError('Invalid queue specifier %r' % qspec)
+ q = q()
+ elif isinstance(qspec, dict):
+ if '()' not in qspec:
+ raise TypeError('Invalid queue specifier %r' % qspec)
+ q = self.configure_custom(dict(qspec))
+ else:
+ raise TypeError('Invalid queue specifier %r' % qspec)
+ config['queue'] = q
+ if 'listener' in config:
+ lspec = config['listener']
+ if isinstance(lspec, type):
+ if not issubclass(lspec, logging.handlers.QueueListener):
+ raise TypeError('Invalid listener specifier %r' % lspec)
+ else:
+ if isinstance(lspec, str):
+ listener = self.resolve(lspec)
+ if isinstance(listener, type) and\
+ not issubclass(listener, logging.handlers.QueueListener):
+ raise TypeError('Invalid listener specifier %r' % lspec)
+ elif isinstance(lspec, dict):
+ if '()' not in lspec:
+ raise TypeError('Invalid listener specifier %r' % lspec)
+ listener = self.configure_custom(dict(lspec))
+ else:
+ raise TypeError('Invalid listener specifier %r' % lspec)
+ if not callable(listener):
+ raise TypeError('Invalid listener specifier %r' % lspec)
+ config['listener'] = listener
+ hlist = []
+ try:
+ for hn in config['handlers']:
+ h = self.config['handlers'][hn]
+ if not isinstance(h, logging.Handler):
+ config.update(config_copy) # restore for deferred cfg
+ raise TypeError('Required handler %r '
+ 'is not configured yet' % hn)
+ hlist.append(h)
+ except Exception as e:
+ raise ValueError('Unable to set required handler %r' % hn) from e
+ config['handlers'] = hlist
elif issubclass(klass, logging.handlers.SMTPHandler) and\
'mailhost' in config:
config['mailhost'] = self.as_tuple(config['mailhost'])
elif issubclass(klass, logging.handlers.SysLogHandler) and\
'address' in config:
config['address'] = self.as_tuple(config['address'])
- factory = klass
+ if issubclass(klass, logging.handlers.QueueHandler):
+ factory = functools.partial(self._configure_queue_handler, klass)
+ else:
+ factory = klass
props = config.pop('.', None)
kwargs = {k: config[k] for k in config if valid_ident(k)}
try: