# Copyright 2001-2019 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,# provided that the above copyright notice appear in all copies and that# both that copyright notice and this permission notice appear in# supporting documentation, and that the name of Vinay Sajip# not be used in advertising or publicity pertaining to distribution# of the software without specific, written prior permission.# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE."""Configuration functions for the logging package for Python. The core packageis based on PEP 282 and comments thereto in comp.lang.python, and influencedby Apache's log4j system.Copyright (C) 2001-2019 Vinay Sajip. All Rights Reserved.To use, simply 'import logging' and log away!"""import errnoimport ioimport loggingimport logging.handlersimport reimport structimport sysimport threadingimport tracebackfrom socketserver import ThreadingTCPServer, StreamRequestHandlerDEFAULT_LOGGING_CONFIG_PORT = 9030RESET_ERROR = errno.ECONNRESET## The following code implements a socket listener for on-the-fly# reconfiguration of logging.## _listener holds the server object doing the listening_listener = Nonedef fileConfig(fname, defaults=None, disable_existing_loggers=True):"""Read the logging configuration from a ConfigParser-format file.This can be called several times from an application, allowing an end userthe ability to select from various pre-canned configurations (if thedeveloper provides a mechanism to present the choices and load the chosenconfiguration)."""import configparserif isinstance(fname, configparser.RawConfigParser):cp = fnameelse:cp = configparser.ConfigParser(defaults)if hasattr(fname, 'readline'):cp.read_file(fname)else:cp.read(fname)formatters = _create_formatters(cp)# critical sectionlogging._acquireLock()try:_clearExistingHandlers()# Handlers add themselves to logging._handlershandlers = _install_handlers(cp, formatters)_install_loggers(cp, handlers, disable_existing_loggers)finally:logging._releaseLock()def _resolve(name):"""Resolve a dotted name to a global object."""name = name.split('.')used = name.pop(0)found = __import__(used)for n in name:used = used + '.' + ntry:found = getattr(found, n)except AttributeError:__import__(used)found = getattr(found, n)return founddef _strip_spaces(alist):return map(str.strip, alist)def _create_formatters(cp):"""Create and return formatters"""flist = cp["formatters"]["keys"]if not len(flist):return {}flist = flist.split(",")flist = _strip_spaces(flist)formatters = {}for form in flist:sectname = "formatter_%s" % formfs = cp.get(sectname, "format", raw=True, fallback=None)dfs = cp.get(sectname, "datefmt", raw=True, fallback=None)stl = cp.get(sectname, "style", raw=True, fallback='%')c = logging.Formatterclass_name = cp[sectname].get("class")if class_name:c = _resolve(class_name)f = c(fs, dfs, stl)formatters[form] = freturn formattersdef _install_handlers(cp, formatters):"""Install and return handlers"""hlist = cp["handlers"]["keys"]if not len(hlist):return {}hlist = hlist.split(",")hlist = _strip_spaces(hlist)handlers = {}fixups = [] #for inter-handler referencesfor hand in hlist:section = cp["handler_%s" % hand]klass = section["class"]fmt = section.get("formatter", "")try:klass = eval(klass, vars(logging))except (AttributeError, NameError):klass = _resolve(klass)args = section.get("args", '()')args = eval(args, vars(logging))kwargs = section.get("kwargs", '{}')kwargs = eval(kwargs, vars(logging))h = klass(*args, **kwargs)if "level" in section:level = section["level"]h.setLevel(level)if len(fmt):h.setFormatter(formatters[fmt])if issubclass(klass, logging.handlers.MemoryHandler):target = section.get("target", "")if len(target): #the target handler may not be loaded yet, so keep for later...fixups.append((h, target))handlers[hand] = h#now all handlers are loaded, fixup inter-handler references...for h, t in fixups:h.setTarget(handlers[t])return handlersdef _handle_existing_loggers(existing, child_loggers, disable_existing):"""When (re)configuring logging, handle loggers which were in the previousconfiguration but are not in the new configuration. There's no pointdeleting them as other threads may continue to hold references to them;and by disabling them, you stop them doing any logging.However, don't disable children of named loggers, as that's probably notwhat was intended by the user. Also, allow existing loggers to NOT bedisabled if disable_existing is false."""root = logging.rootfor log in existing:logger = root.manager.loggerDict[log]if log in child_loggers:if not isinstance(logger, logging.PlaceHolder):logger.setLevel(logging.NOTSET)logger.handlers = []logger.propagate = Trueelse:logger.disabled = disable_existingdef _install_loggers(cp, handlers, disable_existing):"""Create and install loggers"""# configure the root firstllist = cp["loggers"]["keys"]llist = llist.split(",")llist = list(_strip_spaces(llist))llist.remove("root")section = cp["logger_root"]root = logging.rootlog = rootif "level" in section:level = section["level"]log.setLevel(level)for h in root.handlers[:]:root.removeHandler(h)hlist = section["handlers"]if len(hlist):hlist = hlist.split(",")hlist = _strip_spaces(hlist)for hand in hlist:log.addHandler(handlers[hand])#and now the others...#we don't want to lose the existing loggers,#since other threads may have pointers to them.#existing is set to contain all existing loggers,#and as we go through the new configuration we#remove any which are configured. At the end,#what's left in existing is the set of loggers#which were in the previous configuration but#which are not in the new configuration.existing = list(root.manager.loggerDict.keys())#The list needs to be sorted so that we can#avoid disabling child loggers of explicitly#named loggers. With a sorted list it is easier#to find the child loggers.existing.sort()#We'll keep the list of existing loggers#which are children of named loggers here...child_loggers = []#now set up the new ones...for log in llist:section = cp["logger_%s" % log]qn = section["qualname"]propagate = section.getint("propagate", fallback=1)logger = logging.getLogger(qn)if qn in existing:i = existing.index(qn) + 1 # start with the entry after qnprefixed = qn + "."pflen = len(prefixed)num_existing = len(existing)while i < num_existing:if existing[i][:pflen] == prefixed:child_loggers.append(existing[i])i += 1existing.remove(qn)if "level" in section:level = section["level"]logger.setLevel(level)for h in logger.handlers[:]:logger.removeHandler(h)logger.propagate = propagatelogger.disabled = 0hlist = section["handlers"]if len(hlist):hlist = hlist.split(",")hlist = _strip_spaces(hlist)for hand in hlist:logger.addHandler(handlers[hand])#Disable any old loggers. There's no point deleting#them as other threads may continue to hold references#and by disabling them, you stop them doing any logging.#However, don't disable children of named loggers, as that's#probably not what was intended by the user.#for log in existing:# logger = root.manager.loggerDict[log]# if log in child_loggers:# logger.level = logging.NOTSET# logger.handlers = []# logger.propagate = 1# elif disable_existing_loggers:# logger.disabled = 1_handle_existing_loggers(existing, child_loggers, disable_existing)def _clearExistingHandlers():"""Clear and close existing handlers"""logging._handlers.clear()logging.shutdown(logging._handlerList[:])del logging._handlerList[:]IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)def valid_ident(s):m = IDENTIFIER.match(s)if not m:raise ValueError('Not a valid Python identifier: %r' % s)return Trueclass ConvertingMixin(object):"""For ConvertingXXX's, this mixin class provides common functions"""def convert_with_key(self, key, value, replace=True):result = self.configurator.convert(value)#If the converted value is different, save for next timeif value is not result:if replace:self[key] = resultif type(result) in (ConvertingDict, ConvertingList,ConvertingTuple):result.parent = selfresult.key = keyreturn resultdef convert(self, value):result = self.configurator.convert(value)if value is not result:if type(result) in (ConvertingDict, ConvertingList,ConvertingTuple):result.parent = selfreturn result# The ConvertingXXX classes are wrappers around standard Python containers,# and they serve to convert any suitable values in the container. The# conversion converts base dicts, lists and tuples to their wrapped# equivalents, whereas strings which match a conversion format are converted# appropriately.## Each wrapper should have a configurator attribute holding the actual# configurator to use for conversion.class ConvertingDict(dict, ConvertingMixin):"""A converting dictionary wrapper."""def __getitem__(self, key):value = dict.__getitem__(self, key)return self.convert_with_key(key, value)def get(self, key, default=None):value = dict.get(self, key, default)return self.convert_with_key(key, value)def pop(self, key, default=None):value = dict.pop(self, key, default)return self.convert_with_key(key, value, replace=False)class ConvertingList(list, ConvertingMixin):"""A converting list wrapper."""def __getitem__(self, key):value = list.__getitem__(self, key)return self.convert_with_key(key, value)def pop(self, idx=-1):value = list.pop(self, idx)return self.convert(value)class ConvertingTuple(tuple, ConvertingMixin):"""A converting tuple wrapper."""def __getitem__(self, key):value = tuple.__getitem__(self, key)# Can't replace a tuple entry.return self.convert_with_key(key, value, replace=False)class BaseConfigurator(object):"""The configurator base class which defines some useful defaults."""CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')DIGIT_PATTERN = re.compile(r'^\d+$')value_converters = {'ext' : 'ext_convert','cfg' : 'cfg_convert',}# We might want to use a different one, e.g. importlibimporter = staticmethod(__import__)def __init__(self, config):self.config = ConvertingDict(config)self.config.configurator = selfdef resolve(self, s):"""Resolve strings to objects using standard import and attributesyntax."""name = s.split('.')used = name.pop(0)try:found = self.importer(used)for frag in name:used += '.' + fragtry:found = getattr(found, frag)except AttributeError:self.importer(used)found = getattr(found, frag)return foundexcept ImportError:e, tb = sys.exc_info()[1:]v = ValueError('Cannot resolve %r: %s' % (s, e))v.__cause__, v.__traceback__ = e, tbraise vdef ext_convert(self, value):"""Default converter for the ext:// protocol."""return self.resolve(value)def cfg_convert(self, value):"""Default converter for the cfg:// protocol."""rest = valuem = self.WORD_PATTERN.match(rest)if m is None:raise ValueError("Unable to convert %r" % value)else:rest = rest[m.end():]d = self.config[m.groups()[0]]#print d, restwhile rest:m = self.DOT_PATTERN.match(rest)if m:d = d[m.groups()[0]]else:m = self.INDEX_PATTERN.match(rest)if m:idx = m.groups()[0]if not self.DIGIT_PATTERN.match(idx):d = d[idx]else:try:n = int(idx) # try as number first (most likely)d = d[n]except TypeError:d = d[idx]if m:rest = rest[m.end():]else:raise ValueError('Unable to convert ''%r at %r' % (value, rest))#rest should be emptyreturn ddef convert(self, value):"""Convert values to an appropriate type. dicts, lists and tuples arereplaced by their converting alternatives. Strings are checked tosee if they have a conversion format and are converted if they do."""if not isinstance(value, ConvertingDict) and isinstance(value, dict):value = ConvertingDict(value)value.configurator = selfelif not isinstance(value, ConvertingList) and isinstance(value, list):value = ConvertingList(value)value.configurator = selfelif not isinstance(value, ConvertingTuple) and\isinstance(value, tuple):value = ConvertingTuple(value)value.configurator = selfelif isinstance(value, str): # str for py3km = self.CONVERT_PATTERN.match(value)if m:d = m.groupdict()prefix = d['prefix']converter = self.value_converters.get(prefix, None)if converter:suffix = d['suffix']converter = getattr(self, converter)value = converter(suffix)return valuedef configure_custom(self, config):"""Configure an object with a user-supplied factory."""c = config.pop('()')if not callable(c):c = self.resolve(c)props = config.pop('.', None)# Check for valid identifierskwargs = {k: config[k] for k in config if valid_ident(k)}result = c(**kwargs)if props:for name, value in props.items():setattr(result, name, value)return resultdef as_tuple(self, value):"""Utility function which converts lists to tuples."""if isinstance(value, list):value = tuple(value)return valueclass DictConfigurator(BaseConfigurator):"""Configure logging using a dictionary-like object to describe theconfiguration."""def configure(self):"""Do the configuration."""config = self.configif 'version' not in config:raise ValueError("dictionary doesn't specify a version")if config['version'] != 1:raise ValueError("Unsupported version: %s" % config['version'])incremental = config.pop('incremental', False)EMPTY_DICT = {}logging._acquireLock()try:if incremental:handlers = config.get('handlers', EMPTY_DICT)for name in handlers:if name not in logging._handlers:raise ValueError('No handler found with ''name %r' % name)else:try:handler = logging._handlers[name]handler_config = handlers[name]level = handler_config.get('level', None)if level:handler.setLevel(logging._checkLevel(level))except Exception as e:raise ValueError('Unable to configure handler ''%r' % name) from eloggers = config.get('loggers', EMPTY_DICT)for name in loggers:try:self.configure_logger(name, loggers[name], True)except Exception as e:raise ValueError('Unable to configure logger ''%r' % name) from eroot = config.get('root', None)if root:try:self.configure_root(root, True)except Exception as e:raise ValueError('Unable to configure root ''logger') from eelse:disable_existing = config.pop('disable_existing_loggers', True)_clearExistingHandlers()# Do formatters first - they don't refer to anything elseformatters = config.get('formatters', EMPTY_DICT)for name in formatters:try:formatters[name] = self.configure_formatter(formatters[name])except Exception as e:raise ValueError('Unable to configure ''formatter %r' % name) from e# Next, do filters - they don't refer to anything else, eitherfilters = config.get('filters', EMPTY_DICT)for name in filters:try:filters[name] = self.configure_filter(filters[name])except Exception as e:raise ValueError('Unable to configure ''filter %r' % name) from e# Next, do handlers - they refer to formatters and filters# As handlers can refer to other handlers, sort the keys# to allow a deterministic order of configurationhandlers = config.get('handlers', EMPTY_DICT)deferred = []for name in sorted(handlers):try:handler = self.configure_handler(handlers[name])handler.name = namehandlers[name] = handlerexcept Exception as e:if 'target not configured yet' in str(e.__cause__):deferred.append(name)else:raise ValueError('Unable to configure handler ''%r' % name) from e# Now do any that were deferredfor name in deferred:try:handler = self.configure_handler(handlers[name])handler.name = namehandlers[name] = handlerexcept Exception as e:raise ValueError('Unable to configure handler ''%r' % name) from e# Next, do loggers - they refer to handlers and filters#we don't want to lose the existing loggers,#since other threads may have pointers to them.#existing is set to contain all existing loggers,#and as we go through the new configuration we#remove any which are configured. At the end,#what's left in existing is the set of loggers#which were in the previous configuration but#which are not in the new configuration.root = logging.rootexisting = list(root.manager.loggerDict.keys())#The list needs to be sorted so that we can#avoid disabling child loggers of explicitly#named loggers. With a sorted list it is easier#to find the child loggers.existing.sort()#We'll keep the list of existing loggers#which are children of named loggers here...child_loggers = []#now set up the new ones...loggers = config.get('loggers', EMPTY_DICT)for name in loggers:if name in existing:i = existing.index(name) + 1 # look after nameprefixed = name + "."pflen = len(prefixed)num_existing = len(existing)while i < num_existing:if existing[i][:pflen] == prefixed:child_loggers.append(existing[i])i += 1existing.remove(name)try:self.configure_logger(name, loggers[name])except Exception as e:raise ValueError('Unable to configure logger ''%r' % name) from e#Disable any old loggers. There's no point deleting#them as other threads may continue to hold references#and by disabling them, you stop them doing any logging.#However, don't disable children of named loggers, as that's#probably not what was intended by the user.#for log in existing:# logger = root.manager.loggerDict[log]# if log in child_loggers:# logger.level = logging.NOTSET# logger.handlers = []# logger.propagate = True# elif disable_existing:# logger.disabled = True_handle_existing_loggers(existing, child_loggers,disable_existing)# And finally, do the root loggerroot = config.get('root', None)if root:try:self.configure_root(root)except Exception as e:raise ValueError('Unable to configure root ''logger') from efinally:logging._releaseLock()def configure_formatter(self, config):"""Configure a formatter from a dictionary."""if '()' in config:factory = config['()'] # for use in exception handlertry:result = self.configure_custom(config)except TypeError as te:if "'format'" not in str(te):raise#Name of parameter changed from fmt to format.#Retry with old name.#This is so that code can be used with older Python versions#(e.g. by Django)config['fmt'] = config.pop('format')config['()'] = factoryresult = self.configure_custom(config)else:fmt = config.get('format', None)dfmt = config.get('datefmt', None)style = config.get('style', '%')cname = config.get('class', None)if not cname:c = logging.Formatterelse:c = _resolve(cname)# A TypeError would be raised if "validate" key is passed in with a formatter callable# that does not accept "validate" as a parameterif 'validate' in config: # if user hasn't mentioned it, the default will be fineresult = c(fmt, dfmt, style, config['validate'])else:result = c(fmt, dfmt, style)return resultdef configure_filter(self, config):"""Configure a filter from a dictionary."""if '()' in config:result = self.configure_custom(config)else:name = config.get('name', '')result = logging.Filter(name)return resultdef add_filters(self, filterer, filters):"""Add filters to a filterer from a list of names."""for f in filters:try:filterer.addFilter(self.config['filters'][f])except Exception as e:raise ValueError('Unable to add filter %r' % f) from edef configure_handler(self, config):"""Configure a handler from a dictionary."""config_copy = dict(config) # for restoring in case of errorformatter = config.pop('formatter', None)if formatter:try:formatter = self.config['formatters'][formatter]except Exception as e:raise ValueError('Unable to set formatter ''%r' % formatter) from elevel = config.pop('level', None)filters = config.pop('filters', None)if '()' in config:c = config.pop('()')if not callable(c):c = self.resolve(c)factory = celse:cname = config.pop('class')klass = self.resolve(cname)#Special case for handler which refers to another handlerif issubclass(klass, logging.handlers.MemoryHandler) and\'target' in config:try:th = self.config['handlers'][config['target']]if not isinstance(th, logging.Handler):config.update(config_copy) # restore for deferred cfgraise TypeError('target not configured yet')config['target'] = thexcept Exception as e:raise ValueError('Unable to set target handler ''%r' % config['target']) from eelif 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 = klassprops = config.pop('.', None)kwargs = {k: config[k] for k in config if valid_ident(k)}try:result = factory(**kwargs)except TypeError as te:if "'stream'" not in str(te):raise#The argument name changed from strm to stream#Retry with old name.#This is so that code can be used with older Python versions#(e.g. by Django)kwargs['strm'] = kwargs.pop('stream')result = factory(**kwargs)if formatter:result.setFormatter(formatter)if level is not None:result.setLevel(logging._checkLevel(level))if filters:self.add_filters(result, filters)if props:for name, value in props.items():setattr(result, name, value)return resultdef add_handlers(self, logger, handlers):"""Add handlers to a logger from a list of names."""for h in handlers:try:logger.addHandler(self.config['handlers'][h])except Exception as e:raise ValueError('Unable to add handler %r' % h) from edef common_logger_config(self, logger, config, incremental=False):"""Perform configuration which is common to root and non-root loggers."""level = config.get('level', None)if level is not None:logger.setLevel(logging._checkLevel(level))if not incremental:#Remove any existing handlersfor h in logger.handlers[:]:logger.removeHandler(h)handlers = config.get('handlers', None)if handlers:self.add_handlers(logger, handlers)filters = config.get('filters', None)if filters:self.add_filters(logger, filters)def configure_logger(self, name, config, incremental=False):"""Configure a non-root logger from a dictionary."""logger = logging.getLogger(name)self.common_logger_config(logger, config, incremental)propagate = config.get('propagate', None)if propagate is not None:logger.propagate = propagatedef configure_root(self, config, incremental=False):"""Configure a root logger from a dictionary."""root = logging.getLogger()self.common_logger_config(root, config, incremental)dictConfigClass = DictConfiguratordef dictConfig(config):"""Configure logging using a dictionary."""dictConfigClass(config).configure()def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):"""Start up a socket server on the specified port, and listen for newconfigurations.These will be sent as a file suitable for processing by fileConfig().Returns a Thread object on which you can call start() to start the server,and which you can join() when appropriate. To stop the server, callstopListening().Use the ``verify`` argument to verify any bytes received across the wirefrom a client. If specified, it should be a callable which receives asingle argument - the bytes of configuration data received across thenetwork - and it should return either ``None``, to indicate that thepassed in bytes could not be verified and should be discarded, or abyte string which is then passed to the configuration machinery asnormal. Note that you can return transformed bytes, e.g. by decryptingthe bytes passed in."""class ConfigStreamHandler(StreamRequestHandler):"""Handler for a logging configuration request.It expects a completely new logging configuration and uses fileConfigto install it."""def handle(self):"""Handle a request.Each request is expected to be a 4-byte length, packed usingstruct.pack(">L", n), followed by the config file.Uses fileConfig() to do the grunt work."""try:conn = self.connectionchunk = conn.recv(4)if len(chunk) == 4:slen = struct.unpack(">L", chunk)[0]chunk = self.connection.recv(slen)while len(chunk) < slen:chunk = chunk + conn.recv(slen - len(chunk))if self.server.verify is not None:chunk = self.server.verify(chunk)if chunk is not None: # verified, can processchunk = chunk.decode("utf-8")try:import jsond =json.loads(chunk)assert isinstance(d, dict)dictConfig(d)except Exception:#Apply new configuration.file = io.StringIO(chunk)try:fileConfig(file)except Exception:traceback.print_exc()if self.server.ready:self.server.ready.set()except OSError as e:if e.errno != RESET_ERROR:raiseclass ConfigSocketReceiver(ThreadingTCPServer):"""A simple TCP socket-based logging config receiver."""allow_reuse_address = 1def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,handler=None, ready=None, verify=None):ThreadingTCPServer.__init__(self, (host, port), handler)logging._acquireLock()self.abort = 0logging._releaseLock()self.timeout = 1self.ready = readyself.verify = verifydef serve_until_stopped(self):import selectabort = 0while not abort:rd, wr, ex = select.select([self.socket.fileno()],[], [],self.timeout)if rd:self.handle_request()logging._acquireLock()abort = self.abortlogging._releaseLock()self.server_close()class Server(threading.Thread):def __init__(self, rcvr, hdlr, port, verify):super(Server, self).__init__()self.rcvr = rcvrself.hdlr = hdlrself.port = portself.verify = verifyself.ready = threading.Event()def run(self):server = self.rcvr(port=self.port, handler=self.hdlr,ready=self.ready,verify=self.verify)if self.port == 0:self.port = server.server_address[1]self.ready.set()global _listenerlogging._acquireLock()_listener = serverlogging._releaseLock()server.serve_until_stopped()return Server(ConfigSocketReceiver, ConfigStreamHandler, port, verify)def stopListening():"""Stop the listening server which was created with a call to listen()."""global _listenerlogging._acquireLock()try:if _listener:_listener.abort = 1_listener = Nonefinally:logging._releaseLock()
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。