ยปCore Development>Code coverage>Lib/logging/config.py

Python code coverage for Lib/logging/config.py

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