Home | History | Annotate | Download | only in logging
      1 # Copyright 2001-2014 by Vinay Sajip. All Rights Reserved.
      2 #
      3 # Permission to use, copy, modify, and distribute this software and its
      4 # documentation for any purpose and without fee is hereby granted,
      5 # provided that the above copyright notice appear in all copies and that
      6 # both that copyright notice and this permission notice appear in
      7 # supporting documentation, and that the name of Vinay Sajip
      8 # not be used in advertising or publicity pertaining to distribution
      9 # of the software without specific, written prior permission.
     10 # VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
     11 # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
     12 # VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
     13 # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
     14 # IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     15 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     16 
     17 """
     18 Configuration functions for the logging package for Python. The core package
     19 is based on PEP 282 and comments thereto in comp.lang.python, and influenced
     20 by Apache's log4j system.
     21 
     22 Copyright (C) 2001-2014 Vinay Sajip. All Rights Reserved.
     23 
     24 To use, simply 'import logging' and log away!
     25 """
     26 
     27 import cStringIO
     28 import errno
     29 import io
     30 import logging
     31 import logging.handlers
     32 import os
     33 import re
     34 import socket
     35 import struct
     36 import sys
     37 import traceback
     38 import types
     39 
     40 try:
     41     import thread
     42     import threading
     43 except ImportError:
     44     thread = None
     45 
     46 from SocketServer import ThreadingTCPServer, StreamRequestHandler
     47 
     48 
     49 DEFAULT_LOGGING_CONFIG_PORT = 9030
     50 
     51 RESET_ERROR = errno.ECONNRESET
     52 
     53 #
     54 #   The following code implements a socket listener for on-the-fly
     55 #   reconfiguration of logging.
     56 #
     57 #   _listener holds the server object doing the listening
     58 _listener = None
     59 
     60 def fileConfig(fname, defaults=None, disable_existing_loggers=True):
     61     """
     62     Read the logging configuration from a ConfigParser-format file.
     63 
     64     This can be called several times from an application, allowing an end user
     65     the ability to select from various pre-canned configurations (if the
     66     developer provides a mechanism to present the choices and load the chosen
     67     configuration).
     68     """
     69     import ConfigParser
     70 
     71     cp = ConfigParser.ConfigParser(defaults)
     72     if hasattr(fname, 'readline'):
     73         cp.readfp(fname)
     74     else:
     75         cp.read(fname)
     76 
     77     formatters = _create_formatters(cp)
     78 
     79     # critical section
     80     logging._acquireLock()
     81     try:
     82         logging._handlers.clear()
     83         del logging._handlerList[:]
     84         # Handlers add themselves to logging._handlers
     85         handlers = _install_handlers(cp, formatters)
     86         _install_loggers(cp, handlers, disable_existing_loggers)
     87     finally:
     88         logging._releaseLock()
     89 
     90 
     91 def _resolve(name):
     92     """Resolve a dotted name to a global object."""
     93     name = name.split('.')
     94     used = name.pop(0)
     95     found = __import__(used)
     96     for n in name:
     97         used = used + '.' + n
     98         try:
     99             found = getattr(found, n)
    100         except AttributeError:
    101             __import__(used)
    102             found = getattr(found, n)
    103     return found
    104 
    105 def _strip_spaces(alist):
    106     return map(lambda x: x.strip(), alist)
    107 
    108 def _encoded(s):
    109     return s if isinstance(s, str) else s.encode('utf-8')
    110 
    111 def _create_formatters(cp):
    112     """Create and return formatters"""
    113     flist = cp.get("formatters", "keys")
    114     if not len(flist):
    115         return {}
    116     flist = flist.split(",")
    117     flist = _strip_spaces(flist)
    118     formatters = {}
    119     for form in flist:
    120         sectname = "formatter_%s" % form
    121         opts = cp.options(sectname)
    122         if "format" in opts:
    123             fs = cp.get(sectname, "format", 1)
    124         else:
    125             fs = None
    126         if "datefmt" in opts:
    127             dfs = cp.get(sectname, "datefmt", 1)
    128         else:
    129             dfs = None
    130         c = logging.Formatter
    131         if "class" in opts:
    132             class_name = cp.get(sectname, "class")
    133             if class_name:
    134                 c = _resolve(class_name)
    135         f = c(fs, dfs)
    136         formatters[form] = f
    137     return formatters
    138 
    139 
    140 def _install_handlers(cp, formatters):
    141     """Install and return handlers"""
    142     hlist = cp.get("handlers", "keys")
    143     if not len(hlist):
    144         return {}
    145     hlist = hlist.split(",")
    146     hlist = _strip_spaces(hlist)
    147     handlers = {}
    148     fixups = [] #for inter-handler references
    149     for hand in hlist:
    150         sectname = "handler_%s" % hand
    151         klass = cp.get(sectname, "class")
    152         opts = cp.options(sectname)
    153         if "formatter" in opts:
    154             fmt = cp.get(sectname, "formatter")
    155         else:
    156             fmt = ""
    157         try:
    158             klass = eval(klass, vars(logging))
    159         except (AttributeError, NameError):
    160             klass = _resolve(klass)
    161         args = cp.get(sectname, "args")
    162         args = eval(args, vars(logging))
    163         h = klass(*args)
    164         if "level" in opts:
    165             level = cp.get(sectname, "level")
    166             h.setLevel(logging._levelNames[level])
    167         if len(fmt):
    168             h.setFormatter(formatters[fmt])
    169         if issubclass(klass, logging.handlers.MemoryHandler):
    170             if "target" in opts:
    171                 target = cp.get(sectname,"target")
    172             else:
    173                 target = ""
    174             if len(target): #the target handler may not be loaded yet, so keep for later...
    175                 fixups.append((h, target))
    176         handlers[hand] = h
    177     #now all handlers are loaded, fixup inter-handler references...
    178     for h, t in fixups:
    179         h.setTarget(handlers[t])
    180     return handlers
    181 
    182 
    183 def _install_loggers(cp, handlers, disable_existing_loggers):
    184     """Create and install loggers"""
    185 
    186     # configure the root first
    187     llist = cp.get("loggers", "keys")
    188     llist = llist.split(",")
    189     llist = list(map(lambda x: x.strip(), llist))
    190     llist.remove("root")
    191     sectname = "logger_root"
    192     root = logging.root
    193     log = root
    194     opts = cp.options(sectname)
    195     if "level" in opts:
    196         level = cp.get(sectname, "level")
    197         log.setLevel(logging._levelNames[level])
    198     for h in root.handlers[:]:
    199         root.removeHandler(h)
    200     hlist = cp.get(sectname, "handlers")
    201     if len(hlist):
    202         hlist = hlist.split(",")
    203         hlist = _strip_spaces(hlist)
    204         for hand in hlist:
    205             log.addHandler(handlers[hand])
    206 
    207     #and now the others...
    208     #we don't want to lose the existing loggers,
    209     #since other threads may have pointers to them.
    210     #existing is set to contain all existing loggers,
    211     #and as we go through the new configuration we
    212     #remove any which are configured. At the end,
    213     #what's left in existing is the set of loggers
    214     #which were in the previous configuration but
    215     #which are not in the new configuration.
    216     existing = list(root.manager.loggerDict.keys())
    217     #The list needs to be sorted so that we can
    218     #avoid disabling child loggers of explicitly
    219     #named loggers. With a sorted list it is easier
    220     #to find the child loggers.
    221     existing.sort()
    222     #We'll keep the list of existing loggers
    223     #which are children of named loggers here...
    224     child_loggers = []
    225     #now set up the new ones...
    226     for log in llist:
    227         sectname = "logger_%s" % log
    228         qn = cp.get(sectname, "qualname")
    229         opts = cp.options(sectname)
    230         if "propagate" in opts:
    231             propagate = cp.getint(sectname, "propagate")
    232         else:
    233             propagate = 1
    234         logger = logging.getLogger(qn)
    235         if qn in existing:
    236             i = existing.index(qn) + 1 # start with the entry after qn
    237             prefixed = qn + "."
    238             pflen = len(prefixed)
    239             num_existing = len(existing)
    240             while i < num_existing:
    241                 if existing[i][:pflen] == prefixed:
    242                     child_loggers.append(existing[i])
    243                 i += 1
    244             existing.remove(qn)
    245         if "level" in opts:
    246             level = cp.get(sectname, "level")
    247             logger.setLevel(logging._levelNames[level])
    248         for h in logger.handlers[:]:
    249             logger.removeHandler(h)
    250         logger.propagate = propagate
    251         logger.disabled = 0
    252         hlist = cp.get(sectname, "handlers")
    253         if len(hlist):
    254             hlist = hlist.split(",")
    255             hlist = _strip_spaces(hlist)
    256             for hand in hlist:
    257                 logger.addHandler(handlers[hand])
    258 
    259     #Disable any old loggers. There's no point deleting
    260     #them as other threads may continue to hold references
    261     #and by disabling them, you stop them doing any logging.
    262     #However, don't disable children of named loggers, as that's
    263     #probably not what was intended by the user.
    264     for log in existing:
    265         logger = root.manager.loggerDict[log]
    266         if log in child_loggers:
    267             logger.level = logging.NOTSET
    268             logger.handlers = []
    269             logger.propagate = 1
    270         else:
    271             logger.disabled = disable_existing_loggers
    272 
    273 
    274 
    275 IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
    276 
    277 
    278 def valid_ident(s):
    279     m = IDENTIFIER.match(s)
    280     if not m:
    281         raise ValueError('Not a valid Python identifier: %r' % s)
    282     return True
    283 
    284 
    285 class ConvertingMixin(object):
    286     """For ConvertingXXX's, this mixin class provides common functions"""
    287 
    288     def convert_with_key(self, key, value, replace=True):
    289         result = self.configurator.convert(value)
    290         #If the converted value is different, save for next time
    291         if value is not result:
    292             if replace:
    293                 self[key] = result
    294             if type(result) in (ConvertingDict, ConvertingList,
    295                                ConvertingTuple):
    296                 result.parent = self
    297                 result.key = key
    298         return result
    299 
    300     def convert(self, value):
    301         result = self.configurator.convert(value)
    302         if value is not result:
    303             if type(result) in (ConvertingDict, ConvertingList,
    304                                ConvertingTuple):
    305                 result.parent = self
    306         return result
    307 
    308 
    309 # The ConvertingXXX classes are wrappers around standard Python containers,
    310 # and they serve to convert any suitable values in the container. The
    311 # conversion converts base dicts, lists and tuples to their wrapped
    312 # equivalents, whereas strings which match a conversion format are converted
    313 # appropriately.
    314 #
    315 # Each wrapper should have a configurator attribute holding the actual
    316 # configurator to use for conversion.
    317 
    318 class ConvertingDict(dict, ConvertingMixin):
    319     """A converting dictionary wrapper."""
    320 
    321     def __getitem__(self, key):
    322         value = dict.__getitem__(self, key)
    323         return self.convert_with_key(key, value)
    324 
    325     def get(self, key, default=None):
    326         value = dict.get(self, key, default)
    327         return self.convert_with_key(key, value)
    328 
    329     def pop(self, key, default=None):
    330         value = dict.pop(self, key, default)
    331         return self.convert_with_key(key, value, replace=False)
    332 
    333 class ConvertingList(list, ConvertingMixin):
    334     """A converting list wrapper."""
    335     def __getitem__(self, key):
    336         value = list.__getitem__(self, key)
    337         return self.convert_with_key(key, value)
    338 
    339     def pop(self, idx=-1):
    340         value = list.pop(self, idx)
    341         return self.convert(value)
    342 
    343 class ConvertingTuple(tuple, ConvertingMixin):
    344     """A converting tuple wrapper."""
    345     def __getitem__(self, key):
    346         value = tuple.__getitem__(self, key)
    347         # Can't replace a tuple entry.
    348         return self.convert_with_key(key, value, replace=False)
    349 
    350 class BaseConfigurator(object):
    351     """
    352     The configurator base class which defines some useful defaults.
    353     """
    354 
    355     CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
    356 
    357     WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
    358     DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
    359     INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
    360     DIGIT_PATTERN = re.compile(r'^\d+$')
    361 
    362     value_converters = {
    363         'ext' : 'ext_convert',
    364         'cfg' : 'cfg_convert',
    365     }
    366 
    367     # We might want to use a different one, e.g. importlib
    368     importer = __import__
    369 
    370     def __init__(self, config):
    371         self.config = ConvertingDict(config)
    372         self.config.configurator = self
    373         # Issue 12718: winpdb replaces __import__ with a Python function, which
    374         # ends up being treated as a bound method. To avoid problems, we
    375         # set the importer on the instance, but leave it defined in the class
    376         # so existing code doesn't break
    377         if type(__import__) == types.FunctionType:
    378             self.importer = __import__
    379 
    380     def resolve(self, s):
    381         """
    382         Resolve strings to objects using standard import and attribute
    383         syntax.
    384         """
    385         name = s.split('.')
    386         used = name.pop(0)
    387         try:
    388             found = self.importer(used)
    389             for frag in name:
    390                 used += '.' + frag
    391                 try:
    392                     found = getattr(found, frag)
    393                 except AttributeError:
    394                     self.importer(used)
    395                     found = getattr(found, frag)
    396             return found
    397         except ImportError:
    398             e, tb = sys.exc_info()[1:]
    399             v = ValueError('Cannot resolve %r: %s' % (s, e))
    400             v.__cause__, v.__traceback__ = e, tb
    401             raise v
    402 
    403     def ext_convert(self, value):
    404         """Default converter for the ext:// protocol."""
    405         return self.resolve(value)
    406 
    407     def cfg_convert(self, value):
    408         """Default converter for the cfg:// protocol."""
    409         rest = value
    410         m = self.WORD_PATTERN.match(rest)
    411         if m is None:
    412             raise ValueError("Unable to convert %r" % value)
    413         else:
    414             rest = rest[m.end():]
    415             d = self.config[m.groups()[0]]
    416             #print d, rest
    417             while rest:
    418                 m = self.DOT_PATTERN.match(rest)
    419                 if m:
    420                     d = d[m.groups()[0]]
    421                 else:
    422                     m = self.INDEX_PATTERN.match(rest)
    423                     if m:
    424                         idx = m.groups()[0]
    425                         if not self.DIGIT_PATTERN.match(idx):
    426                             d = d[idx]
    427                         else:
    428                             try:
    429                                 n = int(idx) # try as number first (most likely)
    430                                 d = d[n]
    431                             except TypeError:
    432                                 d = d[idx]
    433                 if m:
    434                     rest = rest[m.end():]
    435                 else:
    436                     raise ValueError('Unable to convert '
    437                                      '%r at %r' % (value, rest))
    438         #rest should be empty
    439         return d
    440 
    441     def convert(self, value):
    442         """
    443         Convert values to an appropriate type. dicts, lists and tuples are
    444         replaced by their converting alternatives. Strings are checked to
    445         see if they have a conversion format and are converted if they do.
    446         """
    447         if not isinstance(value, ConvertingDict) and isinstance(value, dict):
    448             value = ConvertingDict(value)
    449             value.configurator = self
    450         elif not isinstance(value, ConvertingList) and isinstance(value, list):
    451             value = ConvertingList(value)
    452             value.configurator = self
    453         elif not isinstance(value, ConvertingTuple) and\
    454                  isinstance(value, tuple):
    455             value = ConvertingTuple(value)
    456             value.configurator = self
    457         elif isinstance(value, basestring): # str for py3k
    458             m = self.CONVERT_PATTERN.match(value)
    459             if m:
    460                 d = m.groupdict()
    461                 prefix = d['prefix']
    462                 converter = self.value_converters.get(prefix, None)
    463                 if converter:
    464                     suffix = d['suffix']
    465                     converter = getattr(self, converter)
    466                     value = converter(suffix)
    467         return value
    468 
    469     def configure_custom(self, config):
    470         """Configure an object with a user-supplied factory."""
    471         c = config.pop('()')
    472         if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
    473             c = self.resolve(c)
    474         props = config.pop('.', None)
    475         # Check for valid identifiers
    476         kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
    477         result = c(**kwargs)
    478         if props:
    479             for name, value in props.items():
    480                 setattr(result, name, value)
    481         return result
    482 
    483     def as_tuple(self, value):
    484         """Utility function which converts lists to tuples."""
    485         if isinstance(value, list):
    486             value = tuple(value)
    487         return value
    488 
    489 class DictConfigurator(BaseConfigurator):
    490     """
    491     Configure logging using a dictionary-like object to describe the
    492     configuration.
    493     """
    494 
    495     def configure(self):
    496         """Do the configuration."""
    497 
    498         config = self.config
    499         if 'version' not in config:
    500             raise ValueError("dictionary doesn't specify a version")
    501         if config['version'] != 1:
    502             raise ValueError("Unsupported version: %s" % config['version'])
    503         incremental = config.pop('incremental', False)
    504         EMPTY_DICT = {}
    505         logging._acquireLock()
    506         try:
    507             if incremental:
    508                 handlers = config.get('handlers', EMPTY_DICT)
    509                 for name in handlers:
    510                     if name not in logging._handlers:
    511                         raise ValueError('No handler found with '
    512                                          'name %r'  % name)
    513                     else:
    514                         try:
    515                             handler = logging._handlers[name]
    516                             handler_config = handlers[name]
    517                             level = handler_config.get('level', None)
    518                             if level:
    519                                 handler.setLevel(logging._checkLevel(level))
    520                         except StandardError as e:
    521                             raise ValueError('Unable to configure handler '
    522                                              '%r: %s' % (name, e))
    523                 loggers = config.get('loggers', EMPTY_DICT)
    524                 for name in loggers:
    525                     try:
    526                         self.configure_logger(name, loggers[name], True)
    527                     except StandardError as e:
    528                         raise ValueError('Unable to configure logger '
    529                                          '%r: %s' % (name, e))
    530                 root = config.get('root', None)
    531                 if root:
    532                     try:
    533                         self.configure_root(root, True)
    534                     except StandardError as e:
    535                         raise ValueError('Unable to configure root '
    536                                          'logger: %s' % e)
    537             else:
    538                 disable_existing = config.pop('disable_existing_loggers', True)
    539 
    540                 logging._handlers.clear()
    541                 del logging._handlerList[:]
    542 
    543                 # Do formatters first - they don't refer to anything else
    544                 formatters = config.get('formatters', EMPTY_DICT)
    545                 for name in formatters:
    546                     try:
    547                         formatters[name] = self.configure_formatter(
    548                                                             formatters[name])
    549                     except StandardError as e:
    550                         raise ValueError('Unable to configure '
    551                                          'formatter %r: %s' % (name, e))
    552                 # Next, do filters - they don't refer to anything else, either
    553                 filters = config.get('filters', EMPTY_DICT)
    554                 for name in filters:
    555                     try:
    556                         filters[name] = self.configure_filter(filters[name])
    557                     except StandardError as e:
    558                         raise ValueError('Unable to configure '
    559                                          'filter %r: %s' % (name, e))
    560 
    561                 # Next, do handlers - they refer to formatters and filters
    562                 # As handlers can refer to other handlers, sort the keys
    563                 # to allow a deterministic order of configuration
    564                 handlers = config.get('handlers', EMPTY_DICT)
    565                 deferred = []
    566                 for name in sorted(handlers):
    567                     try:
    568                         handler = self.configure_handler(handlers[name])
    569                         handler.name = name
    570                         handlers[name] = handler
    571                     except StandardError as e:
    572                         if 'target not configured yet' in str(e):
    573                             deferred.append(name)
    574                         else:
    575                             raise ValueError('Unable to configure handler '
    576                                              '%r: %s' % (name, e))
    577 
    578                 # Now do any that were deferred
    579                 for name in deferred:
    580                     try:
    581                         handler = self.configure_handler(handlers[name])
    582                         handler.name = name
    583                         handlers[name] = handler
    584                     except StandardError as e:
    585                         raise ValueError('Unable to configure handler '
    586                                          '%r: %s' % (name, e))
    587 
    588                 # Next, do loggers - they refer to handlers and filters
    589 
    590                 #we don't want to lose the existing loggers,
    591                 #since other threads may have pointers to them.
    592                 #existing is set to contain all existing loggers,
    593                 #and as we go through the new configuration we
    594                 #remove any which are configured. At the end,
    595                 #what's left in existing is the set of loggers
    596                 #which were in the previous configuration but
    597                 #which are not in the new configuration.
    598                 root = logging.root
    599                 existing = root.manager.loggerDict.keys()
    600                 #The list needs to be sorted so that we can
    601                 #avoid disabling child loggers of explicitly
    602                 #named loggers. With a sorted list it is easier
    603                 #to find the child loggers.
    604                 existing.sort()
    605                 #We'll keep the list of existing loggers
    606                 #which are children of named loggers here...
    607                 child_loggers = []
    608                 #now set up the new ones...
    609                 loggers = config.get('loggers', EMPTY_DICT)
    610                 for name in loggers:
    611                     name = _encoded(name)
    612                     if name in existing:
    613                         i = existing.index(name)
    614                         prefixed = name + "."
    615                         pflen = len(prefixed)
    616                         num_existing = len(existing)
    617                         i = i + 1 # look at the entry after name
    618                         while (i < num_existing) and\
    619                               (existing[i][:pflen] == prefixed):
    620                             child_loggers.append(existing[i])
    621                             i = i + 1
    622                         existing.remove(name)
    623                     try:
    624                         self.configure_logger(name, loggers[name])
    625                     except StandardError as e:
    626                         raise ValueError('Unable to configure logger '
    627                                          '%r: %s' % (name, e))
    628 
    629                 #Disable any old loggers. There's no point deleting
    630                 #them as other threads may continue to hold references
    631                 #and by disabling them, you stop them doing any logging.
    632                 #However, don't disable children of named loggers, as that's
    633                 #probably not what was intended by the user.
    634                 for log in existing:
    635                     logger = root.manager.loggerDict[log]
    636                     if log in child_loggers:
    637                         logger.level = logging.NOTSET
    638                         logger.handlers = []
    639                         logger.propagate = True
    640                     elif disable_existing:
    641                         logger.disabled = True
    642 
    643                 # And finally, do the root logger
    644                 root = config.get('root', None)
    645                 if root:
    646                     try:
    647                         self.configure_root(root)
    648                     except StandardError as e:
    649                         raise ValueError('Unable to configure root '
    650                                          'logger: %s' % e)
    651         finally:
    652             logging._releaseLock()
    653 
    654     def configure_formatter(self, config):
    655         """Configure a formatter from a dictionary."""
    656         if '()' in config:
    657             factory = config['()'] # for use in exception handler
    658             try:
    659                 result = self.configure_custom(config)
    660             except TypeError as te:
    661                 if "'format'" not in str(te):
    662                     raise
    663                 #Name of parameter changed from fmt to format.
    664                 #Retry with old name.
    665                 #This is so that code can be used with older Python versions
    666                 #(e.g. by Django)
    667                 config['fmt'] = config.pop('format')
    668                 config['()'] = factory
    669                 result = self.configure_custom(config)
    670         else:
    671             fmt = config.get('format', None)
    672             dfmt = config.get('datefmt', None)
    673             result = logging.Formatter(fmt, dfmt)
    674         return result
    675 
    676     def configure_filter(self, config):
    677         """Configure a filter from a dictionary."""
    678         if '()' in config:
    679             result = self.configure_custom(config)
    680         else:
    681             name = config.get('name', '')
    682             result = logging.Filter(name)
    683         return result
    684 
    685     def add_filters(self, filterer, filters):
    686         """Add filters to a filterer from a list of names."""
    687         for f in filters:
    688             try:
    689                 filterer.addFilter(self.config['filters'][f])
    690             except StandardError as e:
    691                 raise ValueError('Unable to add filter %r: %s' % (f, e))
    692 
    693     def configure_handler(self, config):
    694         """Configure a handler from a dictionary."""
    695         formatter = config.pop('formatter', None)
    696         if formatter:
    697             try:
    698                 formatter = self.config['formatters'][formatter]
    699             except StandardError as e:
    700                 raise ValueError('Unable to set formatter '
    701                                  '%r: %s' % (formatter, e))
    702         level = config.pop('level', None)
    703         filters = config.pop('filters', None)
    704         if '()' in config:
    705             c = config.pop('()')
    706             if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
    707                 c = self.resolve(c)
    708             factory = c
    709         else:
    710             cname = config.pop('class')
    711             klass = self.resolve(cname)
    712             #Special case for handler which refers to another handler
    713             if issubclass(klass, logging.handlers.MemoryHandler) and\
    714                 'target' in config:
    715                 try:
    716                     th = self.config['handlers'][config['target']]
    717                     if not isinstance(th, logging.Handler):
    718                         config['class'] = cname # restore for deferred configuration
    719                         raise StandardError('target not configured yet')
    720                     config['target'] = th
    721                 except StandardError as e:
    722                     raise ValueError('Unable to set target handler '
    723                                      '%r: %s' % (config['target'], e))
    724             elif issubclass(klass, logging.handlers.SMTPHandler) and\
    725                 'mailhost' in config:
    726                 config['mailhost'] = self.as_tuple(config['mailhost'])
    727             elif issubclass(klass, logging.handlers.SysLogHandler) and\
    728                 'address' in config:
    729                 config['address'] = self.as_tuple(config['address'])
    730             factory = klass
    731         kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
    732         try:
    733             result = factory(**kwargs)
    734         except TypeError as te:
    735             if "'stream'" not in str(te):
    736                 raise
    737             #The argument name changed from strm to stream
    738             #Retry with old name.
    739             #This is so that code can be used with older Python versions
    740             #(e.g. by Django)
    741             kwargs['strm'] = kwargs.pop('stream')
    742             result = factory(**kwargs)
    743         if formatter:
    744             result.setFormatter(formatter)
    745         if level is not None:
    746             result.setLevel(logging._checkLevel(level))
    747         if filters:
    748             self.add_filters(result, filters)
    749         return result
    750 
    751     def add_handlers(self, logger, handlers):
    752         """Add handlers to a logger from a list of names."""
    753         for h in handlers:
    754             try:
    755                 logger.addHandler(self.config['handlers'][h])
    756             except StandardError as e:
    757                 raise ValueError('Unable to add handler %r: %s' % (h, e))
    758 
    759     def common_logger_config(self, logger, config, incremental=False):
    760         """
    761         Perform configuration which is common to root and non-root loggers.
    762         """
    763         level = config.get('level', None)
    764         if level is not None:
    765             logger.setLevel(logging._checkLevel(level))
    766         if not incremental:
    767             #Remove any existing handlers
    768             for h in logger.handlers[:]:
    769                 logger.removeHandler(h)
    770             handlers = config.get('handlers', None)
    771             if handlers:
    772                 self.add_handlers(logger, handlers)
    773             filters = config.get('filters', None)
    774             if filters:
    775                 self.add_filters(logger, filters)
    776 
    777     def configure_logger(self, name, config, incremental=False):
    778         """Configure a non-root logger from a dictionary."""
    779         logger = logging.getLogger(name)
    780         self.common_logger_config(logger, config, incremental)
    781         propagate = config.get('propagate', None)
    782         if propagate is not None:
    783             logger.propagate = propagate
    784 
    785     def configure_root(self, config, incremental=False):
    786         """Configure a root logger from a dictionary."""
    787         root = logging.getLogger()
    788         self.common_logger_config(root, config, incremental)
    789 
    790 dictConfigClass = DictConfigurator
    791 
    792 def dictConfig(config):
    793     """Configure logging using a dictionary."""
    794     dictConfigClass(config).configure()
    795 
    796 
    797 def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
    798     """
    799     Start up a socket server on the specified port, and listen for new
    800     configurations.
    801 
    802     These will be sent as a file suitable for processing by fileConfig().
    803     Returns a Thread object on which you can call start() to start the server,
    804     and which you can join() when appropriate. To stop the server, call
    805     stopListening().
    806     """
    807     if not thread:
    808         raise NotImplementedError("listen() needs threading to work")
    809 
    810     class ConfigStreamHandler(StreamRequestHandler):
    811         """
    812         Handler for a logging configuration request.
    813 
    814         It expects a completely new logging configuration and uses fileConfig
    815         to install it.
    816         """
    817         def handle(self):
    818             """
    819             Handle a request.
    820 
    821             Each request is expected to be a 4-byte length, packed using
    822             struct.pack(">L", n), followed by the config file.
    823             Uses fileConfig() to do the grunt work.
    824             """
    825             import tempfile
    826             try:
    827                 conn = self.connection
    828                 chunk = conn.recv(4)
    829                 if len(chunk) == 4:
    830                     slen = struct.unpack(">L", chunk)[0]
    831                     chunk = self.connection.recv(slen)
    832                     while len(chunk) < slen:
    833                         chunk = chunk + conn.recv(slen - len(chunk))
    834                     try:
    835                         import json
    836                         d =json.loads(chunk)
    837                         assert isinstance(d, dict)
    838                         dictConfig(d)
    839                     except:
    840                         #Apply new configuration.
    841 
    842                         file = cStringIO.StringIO(chunk)
    843                         try:
    844                             fileConfig(file)
    845                         except (KeyboardInterrupt, SystemExit):
    846                             raise
    847                         except:
    848                             traceback.print_exc()
    849                     if self.server.ready:
    850                         self.server.ready.set()
    851             except socket.error as e:
    852                 if e.errno != RESET_ERROR:
    853                     raise
    854 
    855     class ConfigSocketReceiver(ThreadingTCPServer):
    856         """
    857         A simple TCP socket-based logging config receiver.
    858         """
    859 
    860         allow_reuse_address = 1
    861 
    862         def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
    863                      handler=None, ready=None):
    864             ThreadingTCPServer.__init__(self, (host, port), handler)
    865             logging._acquireLock()
    866             self.abort = 0
    867             logging._releaseLock()
    868             self.timeout = 1
    869             self.ready = ready
    870 
    871         def serve_until_stopped(self):
    872             import select
    873             abort = 0
    874             while not abort:
    875                 rd, wr, ex = select.select([self.socket.fileno()],
    876                                            [], [],
    877                                            self.timeout)
    878                 if rd:
    879                     self.handle_request()
    880                 logging._acquireLock()
    881                 abort = self.abort
    882                 logging._releaseLock()
    883             self.socket.close()
    884 
    885     class Server(threading.Thread):
    886 
    887         def __init__(self, rcvr, hdlr, port):
    888             super(Server, self).__init__()
    889             self.rcvr = rcvr
    890             self.hdlr = hdlr
    891             self.port = port
    892             self.ready = threading.Event()
    893 
    894         def run(self):
    895             server = self.rcvr(port=self.port, handler=self.hdlr,
    896                                ready=self.ready)
    897             if self.port == 0:
    898                 self.port = server.server_address[1]
    899             self.ready.set()
    900             global _listener
    901             logging._acquireLock()
    902             _listener = server
    903             logging._releaseLock()
    904             server.serve_until_stopped()
    905 
    906     return Server(ConfigSocketReceiver, ConfigStreamHandler, port)
    907 
    908 def stopListening():
    909     """
    910     Stop the listening server which was created with a call to listen().
    911     """
    912     global _listener
    913     logging._acquireLock()
    914     try:
    915         if _listener:
    916             _listener.abort = 1
    917             _listener = None
    918     finally:
    919         logging._releaseLock()
    920