blob: b3f4e28796a2bc5593b09d8fd708bf497bd6868f [file] [log] [blame]
Vinay Sajipaa275822016-10-03 19:45:50 +01001# Copyright 2001-2016 by Vinay Sajip. All Rights Reserved.
Guido van Rossum57102f82002-11-13 16:15:58 +00002#
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.
Guido van Rossum57102f82002-11-13 16:15:58 +000016
17"""
Vinay Sajip3f742842004-02-28 16:07:46 +000018Configuration functions for the logging package for Python. The core package
19is based on PEP 282 and comments thereto in comp.lang.python, and influenced
20by Apache's log4j system.
Guido van Rossum57102f82002-11-13 16:15:58 +000021
Vinay Sajipaa275822016-10-03 19:45:50 +010022Copyright (C) 2001-2016 Vinay Sajip. All Rights Reserved.
Guido van Rossum57102f82002-11-13 16:15:58 +000023
24To use, simply 'import logging' and log away!
25"""
26
Vinay Sajip71dcb282014-03-20 13:03:17 +000027import errno
Florent Xicluna5252f9f2011-11-07 19:43:05 +010028import io
Vinay Sajip71dcb282014-03-20 13:03:17 +000029import logging
30import logging.handlers
31import re
32import struct
33import sys
34import traceback
Vinay Sajip612df8e2005-02-18 11:54:46 +000035
36try:
Georg Brandl2067bfd2008-05-25 13:05:15 +000037 import _thread as thread
Vinay Sajip612df8e2005-02-18 11:54:46 +000038 import threading
Brett Cannoncd171c82013-07-04 17:43:24 -040039except ImportError: #pragma: no cover
Vinay Sajip612df8e2005-02-18 11:54:46 +000040 thread = None
Guido van Rossum57102f82002-11-13 16:15:58 +000041
Alexandre Vassalottice261952008-05-12 02:31:37 +000042from socketserver import ThreadingTCPServer, StreamRequestHandler
Guido van Rossum57102f82002-11-13 16:15:58 +000043
44
45DEFAULT_LOGGING_CONFIG_PORT = 9030
46
Vinay Sajip71dcb282014-03-20 13:03:17 +000047RESET_ERROR = errno.ECONNRESET
Vinay Sajip326441e2004-02-20 13:16:36 +000048
Guido van Rossum57102f82002-11-13 16:15:58 +000049#
50# The following code implements a socket listener for on-the-fly
51# reconfiguration of logging.
52#
53# _listener holds the server object doing the listening
54_listener = None
55
Georg Brandl472f2e22009-06-08 08:58:54 +000056def fileConfig(fname, defaults=None, disable_existing_loggers=True):
Guido van Rossum57102f82002-11-13 16:15:58 +000057 """
58 Read the logging configuration from a ConfigParser-format file.
59
60 This can be called several times from an application, allowing an end user
61 the ability to select from various pre-canned configurations (if the
62 developer provides a mechanism to present the choices and load the chosen
63 configuration).
Guido van Rossum57102f82002-11-13 16:15:58 +000064 """
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +000065 import configparser
Guido van Rossum57102f82002-11-13 16:15:58 +000066
Vinay Sajipcf9e2f22012-10-09 09:06:03 +010067 if isinstance(fname, configparser.RawConfigParser):
68 cp = fname
Guido van Rossum57102f82002-11-13 16:15:58 +000069 else:
Vinay Sajipcf9e2f22012-10-09 09:06:03 +010070 cp = configparser.ConfigParser(defaults)
71 if hasattr(fname, 'readline'):
72 cp.read_file(fname)
73 else:
74 cp.read(fname)
Vinay Sajip989b69a2006-01-16 21:28:37 +000075
76 formatters = _create_formatters(cp)
77
78 # critical section
Guido van Rossum57102f82002-11-13 16:15:58 +000079 logging._acquireLock()
80 try:
Vinay Sajip989b69a2006-01-16 21:28:37 +000081 logging._handlers.clear()
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000082 del logging._handlerList[:]
Vinay Sajip989b69a2006-01-16 21:28:37 +000083 # Handlers add themselves to logging._handlers
84 handlers = _install_handlers(cp, formatters)
Benjamin Petersonfea6a942008-07-02 16:11:42 +000085 _install_loggers(cp, handlers, disable_existing_loggers)
Guido van Rossum57102f82002-11-13 16:15:58 +000086 finally:
87 logging._releaseLock()
88
Vinay Sajip989b69a2006-01-16 21:28:37 +000089
Vinay Sajip7a7160b2006-01-20 18:28:03 +000090def _resolve(name):
91 """Resolve a dotted name to a global object."""
Neal Norwitz9d72bb42007-04-17 08:48:32 +000092 name = name.split('.')
Vinay Sajip7a7160b2006-01-20 18:28:03 +000093 used = name.pop(0)
94 found = __import__(used)
95 for n in name:
96 used = used + '.' + n
97 try:
98 found = getattr(found, n)
99 except AttributeError:
100 __import__(used)
101 found = getattr(found, n)
102 return found
103
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000104def _strip_spaces(alist):
105 return map(lambda x: x.strip(), alist)
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000106
Vinay Sajip989b69a2006-01-16 21:28:37 +0000107def _create_formatters(cp):
108 """Create and return formatters"""
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000109 flist = cp["formatters"]["keys"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000110 if not len(flist):
111 return {}
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000112 flist = flist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000113 flist = _strip_spaces(flist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000114 formatters = {}
115 for form in flist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000116 sectname = "formatter_%s" % form
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000117 fs = cp.get(sectname, "format", raw=True, fallback=None)
118 dfs = cp.get(sectname, "datefmt", raw=True, fallback=None)
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100119 stl = cp.get(sectname, "style", raw=True, fallback='%')
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000120 c = logging.Formatter
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000121 class_name = cp[sectname].get("class")
122 if class_name:
123 c = _resolve(class_name)
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100124 f = c(fs, dfs, stl)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000125 formatters[form] = f
126 return formatters
127
128
129def _install_handlers(cp, formatters):
130 """Install and return handlers"""
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000131 hlist = cp["handlers"]["keys"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000132 if not len(hlist):
133 return {}
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000134 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000135 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000136 handlers = {}
137 fixups = [] #for inter-handler references
138 for hand in hlist:
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000139 section = cp["handler_%s" % hand]
140 klass = section["class"]
141 fmt = section.get("formatter", "")
Georg Brandl3dbca812008-07-23 16:10:53 +0000142 try:
143 klass = eval(klass, vars(logging))
144 except (AttributeError, NameError):
145 klass = _resolve(klass)
Preston Landers6ea56d22017-08-02 15:44:28 -0500146 args = section.get("args", '()')
Vinay Sajip989b69a2006-01-16 21:28:37 +0000147 args = eval(args, vars(logging))
Preston Landers6ea56d22017-08-02 15:44:28 -0500148 kwargs = section.get("kwargs", '{}')
149 kwargs = eval(kwargs, vars(logging))
150 h = klass(*args, **kwargs)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000151 if "level" in section:
152 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700153 h.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000154 if len(fmt):
155 h.setFormatter(formatters[fmt])
Benjamin Peterson41181742008-07-02 20:22:54 +0000156 if issubclass(klass, logging.handlers.MemoryHandler):
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000157 target = section.get("target", "")
Vinay Sajip989b69a2006-01-16 21:28:37 +0000158 if len(target): #the target handler may not be loaded yet, so keep for later...
159 fixups.append((h, target))
160 handlers[hand] = h
161 #now all handlers are loaded, fixup inter-handler references...
162 for h, t in fixups:
163 h.setTarget(handlers[t])
164 return handlers
165
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000166def _handle_existing_loggers(existing, child_loggers, disable_existing):
167 """
168 When (re)configuring logging, handle loggers which were in the previous
169 configuration but are not in the new configuration. There's no point
170 deleting them as other threads may continue to hold references to them;
171 and by disabling them, you stop them doing any logging.
Vinay Sajip989b69a2006-01-16 21:28:37 +0000172
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000173 However, don't disable children of named loggers, as that's probably not
174 what was intended by the user. Also, allow existing loggers to NOT be
175 disabled if disable_existing is false.
176 """
177 root = logging.root
178 for log in existing:
179 logger = root.manager.loggerDict[log]
180 if log in child_loggers:
181 logger.level = logging.NOTSET
182 logger.handlers = []
183 logger.propagate = True
Vinay Sajip68b4cc82013-03-23 11:18:45 +0000184 else:
185 logger.disabled = disable_existing
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000186
187def _install_loggers(cp, handlers, disable_existing):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000188 """Create and install loggers"""
189
190 # configure the root first
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000191 llist = cp["loggers"]["keys"]
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000192 llist = llist.split(",")
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000193 llist = list(map(lambda x: x.strip(), llist))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000194 llist.remove("root")
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000195 section = cp["logger_root"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000196 root = logging.root
197 log = root
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000198 if "level" in section:
199 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700200 log.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000201 for h in root.handlers[:]:
202 root.removeHandler(h)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000203 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000204 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000205 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000206 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000207 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000208 log.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000209
210 #and now the others...
211 #we don't want to lose the existing loggers,
212 #since other threads may have pointers to them.
213 #existing is set to contain all existing loggers,
214 #and as we go through the new configuration we
215 #remove any which are configured. At the end,
216 #what's left in existing is the set of loggers
217 #which were in the previous configuration but
218 #which are not in the new configuration.
Guido van Rossum8b8a5432007-02-12 00:07:01 +0000219 existing = list(root.manager.loggerDict.keys())
Christian Heimes96f31632007-11-12 01:32:03 +0000220 #The list needs to be sorted so that we can
221 #avoid disabling child loggers of explicitly
222 #named loggers. With a sorted list it is easier
223 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100224 existing.sort()
Christian Heimes96f31632007-11-12 01:32:03 +0000225 #We'll keep the list of existing loggers
226 #which are children of named loggers here...
227 child_loggers = []
Vinay Sajip989b69a2006-01-16 21:28:37 +0000228 #now set up the new ones...
229 for log in llist:
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000230 section = cp["logger_%s" % log]
231 qn = section["qualname"]
232 propagate = section.getint("propagate", fallback=1)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000233 logger = logging.getLogger(qn)
234 if qn in existing:
Vinay Sajip3f84b072011-03-07 17:49:33 +0000235 i = existing.index(qn) + 1 # start with the entry after qn
Christian Heimes96f31632007-11-12 01:32:03 +0000236 prefixed = qn + "."
237 pflen = len(prefixed)
238 num_existing = len(existing)
Vinay Sajip3f84b072011-03-07 17:49:33 +0000239 while i < num_existing:
240 if existing[i][:pflen] == prefixed:
241 child_loggers.append(existing[i])
242 i += 1
Vinay Sajip989b69a2006-01-16 21:28:37 +0000243 existing.remove(qn)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000244 if "level" in section:
245 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700246 logger.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000247 for h in logger.handlers[:]:
248 logger.removeHandler(h)
249 logger.propagate = propagate
250 logger.disabled = 0
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000251 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000252 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000253 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000254 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000255 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000256 logger.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000257
258 #Disable any old loggers. There's no point deleting
259 #them as other threads may continue to hold references
260 #and by disabling them, you stop them doing any logging.
Christian Heimes96f31632007-11-12 01:32:03 +0000261 #However, don't disable children of named loggers, as that's
262 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000263 #for log in existing:
264 # logger = root.manager.loggerDict[log]
265 # if log in child_loggers:
266 # logger.level = logging.NOTSET
267 # logger.handlers = []
268 # logger.propagate = 1
269 # elif disable_existing_loggers:
270 # logger.disabled = 1
271 _handle_existing_loggers(existing, child_loggers, disable_existing)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000272
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000273IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
274
275
276def valid_ident(s):
277 m = IDENTIFIER.match(s)
278 if not m:
279 raise ValueError('Not a valid Python identifier: %r' % s)
280 return True
281
282
Vinay Sajipb1698d42014-03-20 13:14:39 +0000283class ConvertingMixin(object):
284 """For ConvertingXXX's, this mixin class provides common functions"""
285
286 def convert_with_key(self, key, value, replace=True):
287 result = self.configurator.convert(value)
288 #If the converted value is different, save for next time
289 if value is not result:
290 if replace:
291 self[key] = result
292 if type(result) in (ConvertingDict, ConvertingList,
293 ConvertingTuple):
294 result.parent = self
295 result.key = key
296 return result
297
298 def convert(self, value):
299 result = self.configurator.convert(value)
300 if value is not result:
301 if type(result) in (ConvertingDict, ConvertingList,
302 ConvertingTuple):
303 result.parent = self
304 return result
305
306
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000307# The ConvertingXXX classes are wrappers around standard Python containers,
308# and they serve to convert any suitable values in the container. The
309# conversion converts base dicts, lists and tuples to their wrapped
310# equivalents, whereas strings which match a conversion format are converted
311# appropriately.
312#
313# Each wrapper should have a configurator attribute holding the actual
314# configurator to use for conversion.
315
Vinay Sajipb1698d42014-03-20 13:14:39 +0000316class ConvertingDict(dict, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000317 """A converting dictionary wrapper."""
318
319 def __getitem__(self, key):
320 value = dict.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000321 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000322
323 def get(self, key, default=None):
324 value = dict.get(self, key, default)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000325 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000326
327 def pop(self, key, default=None):
328 value = dict.pop(self, key, default)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000329 return self.convert_with_key(key, value, replace=False)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000330
Vinay Sajipb1698d42014-03-20 13:14:39 +0000331class ConvertingList(list, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000332 """A converting list wrapper."""
333 def __getitem__(self, key):
334 value = list.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000335 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000336
337 def pop(self, idx=-1):
338 value = list.pop(self, idx)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000339 return self.convert(value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000340
Vinay Sajipb1698d42014-03-20 13:14:39 +0000341class ConvertingTuple(tuple, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000342 """A converting tuple wrapper."""
343 def __getitem__(self, key):
344 value = tuple.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000345 # Can't replace a tuple entry.
346 return self.convert_with_key(key, value, replace=False)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000347
348class BaseConfigurator(object):
349 """
350 The configurator base class which defines some useful defaults.
351 """
352
353 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
354
355 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
356 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
357 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
358 DIGIT_PATTERN = re.compile(r'^\d+$')
359
360 value_converters = {
361 'ext' : 'ext_convert',
362 'cfg' : 'cfg_convert',
363 }
364
365 # We might want to use a different one, e.g. importlib
Brett Cannonc2368502010-06-12 00:39:28 +0000366 importer = staticmethod(__import__)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000367
368 def __init__(self, config):
369 self.config = ConvertingDict(config)
370 self.config.configurator = self
371
372 def resolve(self, s):
373 """
374 Resolve strings to objects using standard import and attribute
375 syntax.
376 """
377 name = s.split('.')
378 used = name.pop(0)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000379 try:
380 found = self.importer(used)
381 for frag in name:
382 used += '.' + frag
383 try:
384 found = getattr(found, frag)
385 except AttributeError:
386 self.importer(used)
387 found = getattr(found, frag)
388 return found
389 except ImportError:
390 e, tb = sys.exc_info()[1:]
391 v = ValueError('Cannot resolve %r: %s' % (s, e))
392 v.__cause__, v.__traceback__ = e, tb
393 raise v
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000394
395 def ext_convert(self, value):
396 """Default converter for the ext:// protocol."""
397 return self.resolve(value)
398
399 def cfg_convert(self, value):
400 """Default converter for the cfg:// protocol."""
401 rest = value
402 m = self.WORD_PATTERN.match(rest)
403 if m is None:
404 raise ValueError("Unable to convert %r" % value)
405 else:
406 rest = rest[m.end():]
407 d = self.config[m.groups()[0]]
408 #print d, rest
409 while rest:
410 m = self.DOT_PATTERN.match(rest)
411 if m:
412 d = d[m.groups()[0]]
413 else:
414 m = self.INDEX_PATTERN.match(rest)
415 if m:
416 idx = m.groups()[0]
417 if not self.DIGIT_PATTERN.match(idx):
418 d = d[idx]
419 else:
420 try:
421 n = int(idx) # try as number first (most likely)
422 d = d[n]
423 except TypeError:
424 d = d[idx]
425 if m:
426 rest = rest[m.end():]
427 else:
428 raise ValueError('Unable to convert '
429 '%r at %r' % (value, rest))
430 #rest should be empty
431 return d
432
433 def convert(self, value):
434 """
435 Convert values to an appropriate type. dicts, lists and tuples are
436 replaced by their converting alternatives. Strings are checked to
437 see if they have a conversion format and are converted if they do.
438 """
439 if not isinstance(value, ConvertingDict) and isinstance(value, dict):
440 value = ConvertingDict(value)
441 value.configurator = self
442 elif not isinstance(value, ConvertingList) and isinstance(value, list):
443 value = ConvertingList(value)
444 value.configurator = self
445 elif not isinstance(value, ConvertingTuple) and\
446 isinstance(value, tuple):
447 value = ConvertingTuple(value)
448 value.configurator = self
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000449 elif isinstance(value, str): # str for py3k
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000450 m = self.CONVERT_PATTERN.match(value)
451 if m:
452 d = m.groupdict()
453 prefix = d['prefix']
454 converter = self.value_converters.get(prefix, None)
455 if converter:
456 suffix = d['suffix']
457 converter = getattr(self, converter)
458 value = converter(suffix)
459 return value
460
461 def configure_custom(self, config):
462 """Configure an object with a user-supplied factory."""
463 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200464 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000465 c = self.resolve(c)
466 props = config.pop('.', None)
467 # Check for valid identifiers
Jon Dufresne39726282017-05-18 07:35:54 -0700468 kwargs = dict((k, config[k]) for k in config if valid_ident(k))
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000469 result = c(**kwargs)
470 if props:
471 for name, value in props.items():
472 setattr(result, name, value)
473 return result
474
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000475 def as_tuple(self, value):
476 """Utility function which converts lists to tuples."""
477 if isinstance(value, list):
478 value = tuple(value)
479 return value
480
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000481class DictConfigurator(BaseConfigurator):
482 """
483 Configure logging using a dictionary-like object to describe the
484 configuration.
485 """
486
487 def configure(self):
488 """Do the configuration."""
489
490 config = self.config
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000491 if 'version' not in config:
492 raise ValueError("dictionary doesn't specify a version")
493 if config['version'] != 1:
494 raise ValueError("Unsupported version: %s" % config['version'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000495 incremental = config.pop('incremental', False)
496 EMPTY_DICT = {}
497 logging._acquireLock()
498 try:
499 if incremental:
500 handlers = config.get('handlers', EMPTY_DICT)
501 for name in handlers:
502 if name not in logging._handlers:
503 raise ValueError('No handler found with '
504 'name %r' % name)
505 else:
506 try:
507 handler = logging._handlers[name]
508 handler_config = handlers[name]
509 level = handler_config.get('level', None)
510 if level:
511 handler.setLevel(logging._checkLevel(level))
512 except Exception as e:
513 raise ValueError('Unable to configure handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100514 '%r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000515 loggers = config.get('loggers', EMPTY_DICT)
516 for name in loggers:
517 try:
518 self.configure_logger(name, loggers[name], True)
519 except Exception as e:
520 raise ValueError('Unable to configure logger '
Vinay Sajipaa275822016-10-03 19:45:50 +0100521 '%r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000522 root = config.get('root', None)
523 if root:
524 try:
525 self.configure_root(root, True)
526 except Exception as e:
527 raise ValueError('Unable to configure root '
Vinay Sajipaa275822016-10-03 19:45:50 +0100528 'logger') from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000529 else:
530 disable_existing = config.pop('disable_existing_loggers', True)
531
532 logging._handlers.clear()
533 del logging._handlerList[:]
534
535 # Do formatters first - they don't refer to anything else
536 formatters = config.get('formatters', EMPTY_DICT)
537 for name in formatters:
538 try:
539 formatters[name] = self.configure_formatter(
540 formatters[name])
541 except Exception as e:
542 raise ValueError('Unable to configure '
Vinay Sajipaa275822016-10-03 19:45:50 +0100543 'formatter %r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000544 # Next, do filters - they don't refer to anything else, either
545 filters = config.get('filters', EMPTY_DICT)
546 for name in filters:
547 try:
548 filters[name] = self.configure_filter(filters[name])
549 except Exception as e:
550 raise ValueError('Unable to configure '
Vinay Sajipaa275822016-10-03 19:45:50 +0100551 'filter %r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000552
553 # Next, do handlers - they refer to formatters and filters
554 # As handlers can refer to other handlers, sort the keys
555 # to allow a deterministic order of configuration
556 handlers = config.get('handlers', EMPTY_DICT)
Vinay Sajip3f885b52013-03-22 15:19:54 +0000557 deferred = []
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000558 for name in sorted(handlers):
559 try:
560 handler = self.configure_handler(handlers[name])
561 handler.name = name
562 handlers[name] = handler
563 except Exception as e:
Vinay Sajipb7403432016-10-03 19:50:56 +0100564 if 'target not configured yet' in str(e.__cause__):
Vinay Sajip3f885b52013-03-22 15:19:54 +0000565 deferred.append(name)
566 else:
567 raise ValueError('Unable to configure handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100568 '%r' % name) from e
Vinay Sajip3f885b52013-03-22 15:19:54 +0000569
570 # Now do any that were deferred
571 for name in deferred:
572 try:
573 handler = self.configure_handler(handlers[name])
574 handler.name = name
575 handlers[name] = handler
576 except Exception as e:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000577 raise ValueError('Unable to configure handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100578 '%r' % name) from e
Vinay Sajip3f885b52013-03-22 15:19:54 +0000579
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000580 # Next, do loggers - they refer to handlers and filters
581
582 #we don't want to lose the existing loggers,
583 #since other threads may have pointers to them.
584 #existing is set to contain all existing loggers,
585 #and as we go through the new configuration we
586 #remove any which are configured. At the end,
587 #what's left in existing is the set of loggers
588 #which were in the previous configuration but
589 #which are not in the new configuration.
590 root = logging.root
591 existing = list(root.manager.loggerDict.keys())
592 #The list needs to be sorted so that we can
593 #avoid disabling child loggers of explicitly
594 #named loggers. With a sorted list it is easier
595 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100596 existing.sort()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000597 #We'll keep the list of existing loggers
598 #which are children of named loggers here...
599 child_loggers = []
600 #now set up the new ones...
601 loggers = config.get('loggers', EMPTY_DICT)
602 for name in loggers:
603 if name in existing:
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000604 i = existing.index(name) + 1 # look after name
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000605 prefixed = name + "."
606 pflen = len(prefixed)
607 num_existing = len(existing)
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000608 while i < num_existing:
609 if existing[i][:pflen] == prefixed:
610 child_loggers.append(existing[i])
611 i += 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000612 existing.remove(name)
613 try:
614 self.configure_logger(name, loggers[name])
615 except Exception as e:
616 raise ValueError('Unable to configure logger '
Vinay Sajipaa275822016-10-03 19:45:50 +0100617 '%r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000618
619 #Disable any old loggers. There's no point deleting
620 #them as other threads may continue to hold references
621 #and by disabling them, you stop them doing any logging.
622 #However, don't disable children of named loggers, as that's
623 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000624 #for log in existing:
625 # logger = root.manager.loggerDict[log]
626 # if log in child_loggers:
627 # logger.level = logging.NOTSET
628 # logger.handlers = []
629 # logger.propagate = True
630 # elif disable_existing:
631 # logger.disabled = True
632 _handle_existing_loggers(existing, child_loggers,
633 disable_existing)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000634
635 # And finally, do the root logger
636 root = config.get('root', None)
637 if root:
638 try:
639 self.configure_root(root)
640 except Exception as e:
641 raise ValueError('Unable to configure root '
Vinay Sajipaa275822016-10-03 19:45:50 +0100642 'logger') from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000643 finally:
644 logging._releaseLock()
645
646 def configure_formatter(self, config):
647 """Configure a formatter from a dictionary."""
648 if '()' in config:
649 factory = config['()'] # for use in exception handler
650 try:
651 result = self.configure_custom(config)
652 except TypeError as te:
653 if "'format'" not in str(te):
654 raise
655 #Name of parameter changed from fmt to format.
656 #Retry with old name.
657 #This is so that code can be used with older Python versions
658 #(e.g. by Django)
659 config['fmt'] = config.pop('format')
660 config['()'] = factory
661 result = self.configure_custom(config)
662 else:
663 fmt = config.get('format', None)
664 dfmt = config.get('datefmt', None)
Vinay Sajip28421c62013-03-29 17:56:54 +0000665 style = config.get('style', '%')
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100666 cname = config.get('class', None)
667 if not cname:
668 c = logging.Formatter
669 else:
670 c = _resolve(cname)
671 result = c(fmt, dfmt, style)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000672 return result
673
674 def configure_filter(self, config):
675 """Configure a filter from a dictionary."""
676 if '()' in config:
677 result = self.configure_custom(config)
678 else:
679 name = config.get('name', '')
680 result = logging.Filter(name)
681 return result
682
683 def add_filters(self, filterer, filters):
684 """Add filters to a filterer from a list of names."""
685 for f in filters:
686 try:
687 filterer.addFilter(self.config['filters'][f])
688 except Exception as e:
Vinay Sajipaa275822016-10-03 19:45:50 +0100689 raise ValueError('Unable to add filter %r' % f) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000690
691 def configure_handler(self, config):
692 """Configure a handler from a dictionary."""
Vinay Sajip28421c62013-03-29 17:56:54 +0000693 config_copy = dict(config) # for restoring in case of error
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000694 formatter = config.pop('formatter', None)
695 if formatter:
696 try:
697 formatter = self.config['formatters'][formatter]
698 except Exception as e:
699 raise ValueError('Unable to set formatter '
Vinay Sajipaa275822016-10-03 19:45:50 +0100700 '%r' % formatter) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000701 level = config.pop('level', None)
702 filters = config.pop('filters', None)
703 if '()' in config:
704 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200705 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000706 c = self.resolve(c)
707 factory = c
708 else:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000709 cname = config.pop('class')
710 klass = self.resolve(cname)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000711 #Special case for handler which refers to another handler
712 if issubclass(klass, logging.handlers.MemoryHandler) and\
713 'target' in config:
714 try:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000715 th = self.config['handlers'][config['target']]
716 if not isinstance(th, logging.Handler):
Vinay Sajip28421c62013-03-29 17:56:54 +0000717 config.update(config_copy) # restore for deferred cfg
Vinay Sajip3f885b52013-03-22 15:19:54 +0000718 raise TypeError('target not configured yet')
719 config['target'] = th
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000720 except Exception as e:
721 raise ValueError('Unable to set target handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100722 '%r' % config['target']) from e
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000723 elif issubclass(klass, logging.handlers.SMTPHandler) and\
724 'mailhost' in config:
725 config['mailhost'] = self.as_tuple(config['mailhost'])
726 elif issubclass(klass, logging.handlers.SysLogHandler) and\
727 'address' in config:
728 config['address'] = self.as_tuple(config['address'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000729 factory = klass
Vinay Sajip8d270232012-11-15 14:20:18 +0000730 props = config.pop('.', None)
Jon Dufresne39726282017-05-18 07:35:54 -0700731 kwargs = dict((k, config[k]) for k in config if valid_ident(k))
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000732 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)
Vinay Sajip8d270232012-11-15 14:20:18 +0000749 if props:
750 for name, value in props.items():
751 setattr(result, name, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000752 return result
753
754 def add_handlers(self, logger, handlers):
755 """Add handlers to a logger from a list of names."""
756 for h in handlers:
757 try:
758 logger.addHandler(self.config['handlers'][h])
759 except Exception as e:
Vinay Sajipaa275822016-10-03 19:45:50 +0100760 raise ValueError('Unable to add handler %r' % h) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000761
762 def common_logger_config(self, logger, config, incremental=False):
763 """
764 Perform configuration which is common to root and non-root loggers.
765 """
766 level = config.get('level', None)
767 if level is not None:
768 logger.setLevel(logging._checkLevel(level))
769 if not incremental:
770 #Remove any existing handlers
771 for h in logger.handlers[:]:
772 logger.removeHandler(h)
773 handlers = config.get('handlers', None)
774 if handlers:
775 self.add_handlers(logger, handlers)
776 filters = config.get('filters', None)
777 if filters:
778 self.add_filters(logger, filters)
779
780 def configure_logger(self, name, config, incremental=False):
781 """Configure a non-root logger from a dictionary."""
782 logger = logging.getLogger(name)
783 self.common_logger_config(logger, config, incremental)
784 propagate = config.get('propagate', None)
785 if propagate is not None:
786 logger.propagate = propagate
787
788 def configure_root(self, config, incremental=False):
789 """Configure a root logger from a dictionary."""
790 root = logging.getLogger()
791 self.common_logger_config(root, config, incremental)
792
793dictConfigClass = DictConfigurator
794
795def dictConfig(config):
796 """Configure logging using a dictionary."""
797 dictConfigClass(config).configure()
798
799
Vinay Sajip4ded5512012-10-02 15:56:16 +0100800def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000801 """
802 Start up a socket server on the specified port, and listen for new
803 configurations.
804
805 These will be sent as a file suitable for processing by fileConfig().
806 Returns a Thread object on which you can call start() to start the server,
807 and which you can join() when appropriate. To stop the server, call
808 stopListening().
Vinay Sajip3e763da2012-10-02 16:15:33 +0100809
810 Use the ``verify`` argument to verify any bytes received across the wire
811 from a client. If specified, it should be a callable which receives a
812 single argument - the bytes of configuration data received across the
813 network - and it should return either ``None``, to indicate that the
814 passed in bytes could not be verified and should be discarded, or a
815 byte string which is then passed to the configuration machinery as
816 normal. Note that you can return transformed bytes, e.g. by decrypting
817 the bytes passed in.
Guido van Rossum57102f82002-11-13 16:15:58 +0000818 """
Vinay Sajip985ef872011-04-26 19:34:04 +0100819 if not thread: #pragma: no cover
Collin Winterce36ad82007-08-30 01:19:48 +0000820 raise NotImplementedError("listen() needs threading to work")
Guido van Rossum57102f82002-11-13 16:15:58 +0000821
822 class ConfigStreamHandler(StreamRequestHandler):
823 """
824 Handler for a logging configuration request.
825
826 It expects a completely new logging configuration and uses fileConfig
827 to install it.
828 """
829 def handle(self):
830 """
831 Handle a request.
832
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000833 Each request is expected to be a 4-byte length, packed using
834 struct.pack(">L", n), followed by the config file.
835 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000836 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000837 try:
838 conn = self.connection
839 chunk = conn.recv(4)
840 if len(chunk) == 4:
841 slen = struct.unpack(">L", chunk)[0]
842 chunk = self.connection.recv(slen)
843 while len(chunk) < slen:
844 chunk = chunk + conn.recv(slen - len(chunk))
Vinay Sajip4ded5512012-10-02 15:56:16 +0100845 if self.server.verify is not None:
846 chunk = self.server.verify(chunk)
847 if chunk is not None: # verified, can process
848 chunk = chunk.decode("utf-8")
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000849 try:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100850 import json
851 d =json.loads(chunk)
852 assert isinstance(d, dict)
853 dictConfig(d)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100854 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100855 #Apply new configuration.
856
857 file = io.StringIO(chunk)
858 try:
859 fileConfig(file)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100860 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100861 traceback.print_exc()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000862 if self.server.ready:
863 self.server.ready.set()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200864 except OSError as e:
Vinay Sajip71dcb282014-03-20 13:03:17 +0000865 if e.errno != RESET_ERROR:
Guido van Rossum57102f82002-11-13 16:15:58 +0000866 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000867
868 class ConfigSocketReceiver(ThreadingTCPServer):
869 """
870 A simple TCP socket-based logging config receiver.
871 """
872
873 allow_reuse_address = 1
874
875 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100876 handler=None, ready=None, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000877 ThreadingTCPServer.__init__(self, (host, port), handler)
878 logging._acquireLock()
879 self.abort = 0
880 logging._releaseLock()
881 self.timeout = 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000882 self.ready = ready
Vinay Sajip4ded5512012-10-02 15:56:16 +0100883 self.verify = verify
Guido van Rossum57102f82002-11-13 16:15:58 +0000884
885 def serve_until_stopped(self):
886 import select
887 abort = 0
888 while not abort:
889 rd, wr, ex = select.select([self.socket.fileno()],
890 [], [],
891 self.timeout)
892 if rd:
893 self.handle_request()
894 logging._acquireLock()
895 abort = self.abort
896 logging._releaseLock()
Brian Curtin6ff2a7d2010-10-31 04:40:53 +0000897 self.socket.close()
Guido van Rossum57102f82002-11-13 16:15:58 +0000898
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000899 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000900
Vinay Sajip4ded5512012-10-02 15:56:16 +0100901 def __init__(self, rcvr, hdlr, port, verify):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000902 super(Server, self).__init__()
903 self.rcvr = rcvr
904 self.hdlr = hdlr
905 self.port = port
Vinay Sajip4ded5512012-10-02 15:56:16 +0100906 self.verify = verify
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000907 self.ready = threading.Event()
908
909 def run(self):
910 server = self.rcvr(port=self.port, handler=self.hdlr,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100911 ready=self.ready,
912 verify=self.verify)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000913 if self.port == 0:
914 self.port = server.server_address[1]
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000915 self.ready.set()
916 global _listener
917 logging._acquireLock()
918 _listener = server
919 logging._releaseLock()
920 server.serve_until_stopped()
921
Vinay Sajip4ded5512012-10-02 15:56:16 +0100922 return Server(ConfigSocketReceiver, ConfigStreamHandler, port, verify)
Guido van Rossum57102f82002-11-13 16:15:58 +0000923
924def stopListening():
925 """
926 Stop the listening server which was created with a call to listen().
927 """
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000928 global _listener
Vinay Sajip9fdd11b2010-09-25 17:48:25 +0000929 logging._acquireLock()
930 try:
931 if _listener:
932 _listener.abort = 1
933 _listener = None
934 finally:
Guido van Rossum57102f82002-11-13 16:15:58 +0000935 logging._releaseLock()