blob: fa1a398aee2a2b1d5385e5aed78adf032e6aa5c5 [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
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020034import threading
Vinay Sajip71dcb282014-03-20 13:03:17 +000035import traceback
Vinay Sajip612df8e2005-02-18 11:54:46 +000036
Alexandre Vassalottice261952008-05-12 02:31:37 +000037from socketserver import ThreadingTCPServer, StreamRequestHandler
Guido van Rossum57102f82002-11-13 16:15:58 +000038
39
40DEFAULT_LOGGING_CONFIG_PORT = 9030
41
Vinay Sajip71dcb282014-03-20 13:03:17 +000042RESET_ERROR = errno.ECONNRESET
Vinay Sajip326441e2004-02-20 13:16:36 +000043
Guido van Rossum57102f82002-11-13 16:15:58 +000044#
45# The following code implements a socket listener for on-the-fly
46# reconfiguration of logging.
47#
48# _listener holds the server object doing the listening
49_listener = None
50
Georg Brandl472f2e22009-06-08 08:58:54 +000051def fileConfig(fname, defaults=None, disable_existing_loggers=True):
Guido van Rossum57102f82002-11-13 16:15:58 +000052 """
53 Read the logging configuration from a ConfigParser-format file.
54
55 This can be called several times from an application, allowing an end user
56 the ability to select from various pre-canned configurations (if the
57 developer provides a mechanism to present the choices and load the chosen
58 configuration).
Guido van Rossum57102f82002-11-13 16:15:58 +000059 """
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +000060 import configparser
Guido van Rossum57102f82002-11-13 16:15:58 +000061
Vinay Sajipcf9e2f22012-10-09 09:06:03 +010062 if isinstance(fname, configparser.RawConfigParser):
63 cp = fname
Guido van Rossum57102f82002-11-13 16:15:58 +000064 else:
Vinay Sajipcf9e2f22012-10-09 09:06:03 +010065 cp = configparser.ConfigParser(defaults)
66 if hasattr(fname, 'readline'):
67 cp.read_file(fname)
68 else:
69 cp.read(fname)
Vinay Sajip989b69a2006-01-16 21:28:37 +000070
71 formatters = _create_formatters(cp)
72
73 # critical section
Guido van Rossum57102f82002-11-13 16:15:58 +000074 logging._acquireLock()
75 try:
Xtreak087570a2018-07-02 14:27:46 +053076 _clearExistingHandlers()
77
Vinay Sajip989b69a2006-01-16 21:28:37 +000078 # Handlers add themselves to logging._handlers
79 handlers = _install_handlers(cp, formatters)
Benjamin Petersonfea6a942008-07-02 16:11:42 +000080 _install_loggers(cp, handlers, disable_existing_loggers)
Guido van Rossum57102f82002-11-13 16:15:58 +000081 finally:
82 logging._releaseLock()
83
Vinay Sajip989b69a2006-01-16 21:28:37 +000084
Vinay Sajip7a7160b2006-01-20 18:28:03 +000085def _resolve(name):
86 """Resolve a dotted name to a global object."""
Neal Norwitz9d72bb42007-04-17 08:48:32 +000087 name = name.split('.')
Vinay Sajip7a7160b2006-01-20 18:28:03 +000088 used = name.pop(0)
89 found = __import__(used)
90 for n in name:
91 used = used + '.' + n
92 try:
93 found = getattr(found, n)
94 except AttributeError:
95 __import__(used)
96 found = getattr(found, n)
97 return found
98
Benjamin Petersonae5360b2008-09-08 23:05:23 +000099def _strip_spaces(alist):
sanjayp2ae4ad72017-11-15 14:58:11 +0530100 return map(str.strip, alist)
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000101
Vinay Sajip989b69a2006-01-16 21:28:37 +0000102def _create_formatters(cp):
103 """Create and return formatters"""
Łukasz Langa26d513c2010-11-10 18:57:39 +0000104 flist = cp["formatters"]["keys"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000105 if not len(flist):
106 return {}
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000107 flist = flist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000108 flist = _strip_spaces(flist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000109 formatters = {}
110 for form in flist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000111 sectname = "formatter_%s" % form
Łukasz Langa26d513c2010-11-10 18:57:39 +0000112 fs = cp.get(sectname, "format", raw=True, fallback=None)
113 dfs = cp.get(sectname, "datefmt", raw=True, fallback=None)
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100114 stl = cp.get(sectname, "style", raw=True, fallback='%')
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000115 c = logging.Formatter
Łukasz Langa26d513c2010-11-10 18:57:39 +0000116 class_name = cp[sectname].get("class")
117 if class_name:
118 c = _resolve(class_name)
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100119 f = c(fs, dfs, stl)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000120 formatters[form] = f
121 return formatters
122
123
124def _install_handlers(cp, formatters):
125 """Install and return handlers"""
Łukasz Langa26d513c2010-11-10 18:57:39 +0000126 hlist = cp["handlers"]["keys"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000127 if not len(hlist):
128 return {}
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000129 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000130 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000131 handlers = {}
132 fixups = [] #for inter-handler references
133 for hand in hlist:
Łukasz Langa26d513c2010-11-10 18:57:39 +0000134 section = cp["handler_%s" % hand]
135 klass = section["class"]
136 fmt = section.get("formatter", "")
Georg Brandl3dbca812008-07-23 16:10:53 +0000137 try:
138 klass = eval(klass, vars(logging))
139 except (AttributeError, NameError):
140 klass = _resolve(klass)
Preston Landers6ea56d22017-08-02 15:44:28 -0500141 args = section.get("args", '()')
Vinay Sajip989b69a2006-01-16 21:28:37 +0000142 args = eval(args, vars(logging))
Preston Landers6ea56d22017-08-02 15:44:28 -0500143 kwargs = section.get("kwargs", '{}')
144 kwargs = eval(kwargs, vars(logging))
145 h = klass(*args, **kwargs)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000146 if "level" in section:
147 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700148 h.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000149 if len(fmt):
150 h.setFormatter(formatters[fmt])
Benjamin Peterson41181742008-07-02 20:22:54 +0000151 if issubclass(klass, logging.handlers.MemoryHandler):
Łukasz Langa26d513c2010-11-10 18:57:39 +0000152 target = section.get("target", "")
Vinay Sajip989b69a2006-01-16 21:28:37 +0000153 if len(target): #the target handler may not be loaded yet, so keep for later...
154 fixups.append((h, target))
155 handlers[hand] = h
156 #now all handlers are loaded, fixup inter-handler references...
157 for h, t in fixups:
158 h.setTarget(handlers[t])
159 return handlers
160
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000161def _handle_existing_loggers(existing, child_loggers, disable_existing):
162 """
163 When (re)configuring logging, handle loggers which were in the previous
164 configuration but are not in the new configuration. There's no point
165 deleting them as other threads may continue to hold references to them;
166 and by disabling them, you stop them doing any logging.
Vinay Sajip989b69a2006-01-16 21:28:37 +0000167
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000168 However, don't disable children of named loggers, as that's probably not
169 what was intended by the user. Also, allow existing loggers to NOT be
170 disabled if disable_existing is false.
171 """
172 root = logging.root
173 for log in existing:
174 logger = root.manager.loggerDict[log]
175 if log in child_loggers:
176 logger.level = logging.NOTSET
177 logger.handlers = []
178 logger.propagate = True
Vinay Sajip68b4cc82013-03-23 11:18:45 +0000179 else:
180 logger.disabled = disable_existing
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000181
182def _install_loggers(cp, handlers, disable_existing):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000183 """Create and install loggers"""
184
185 # configure the root first
Łukasz Langa26d513c2010-11-10 18:57:39 +0000186 llist = cp["loggers"]["keys"]
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000187 llist = llist.split(",")
sanjayp2ae4ad72017-11-15 14:58:11 +0530188 llist = list(_strip_spaces(llist))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000189 llist.remove("root")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000190 section = cp["logger_root"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000191 root = logging.root
192 log = root
Łukasz Langa26d513c2010-11-10 18:57:39 +0000193 if "level" in section:
194 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700195 log.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000196 for h in root.handlers[:]:
197 root.removeHandler(h)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000198 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000199 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000200 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000201 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000202 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000203 log.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000204
205 #and now the others...
206 #we don't want to lose the existing loggers,
207 #since other threads may have pointers to them.
208 #existing is set to contain all existing loggers,
209 #and as we go through the new configuration we
210 #remove any which are configured. At the end,
211 #what's left in existing is the set of loggers
212 #which were in the previous configuration but
213 #which are not in the new configuration.
Guido van Rossum8b8a5432007-02-12 00:07:01 +0000214 existing = list(root.manager.loggerDict.keys())
Christian Heimes96f31632007-11-12 01:32:03 +0000215 #The list needs to be sorted so that we can
216 #avoid disabling child loggers of explicitly
217 #named loggers. With a sorted list it is easier
218 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100219 existing.sort()
Christian Heimes96f31632007-11-12 01:32:03 +0000220 #We'll keep the list of existing loggers
221 #which are children of named loggers here...
222 child_loggers = []
Vinay Sajip989b69a2006-01-16 21:28:37 +0000223 #now set up the new ones...
224 for log in llist:
Łukasz Langa26d513c2010-11-10 18:57:39 +0000225 section = cp["logger_%s" % log]
226 qn = section["qualname"]
227 propagate = section.getint("propagate", fallback=1)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000228 logger = logging.getLogger(qn)
229 if qn in existing:
Vinay Sajip3f84b072011-03-07 17:49:33 +0000230 i = existing.index(qn) + 1 # start with the entry after qn
Christian Heimes96f31632007-11-12 01:32:03 +0000231 prefixed = qn + "."
232 pflen = len(prefixed)
233 num_existing = len(existing)
Vinay Sajip3f84b072011-03-07 17:49:33 +0000234 while i < num_existing:
235 if existing[i][:pflen] == prefixed:
236 child_loggers.append(existing[i])
237 i += 1
Vinay Sajip989b69a2006-01-16 21:28:37 +0000238 existing.remove(qn)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000239 if "level" in section:
240 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700241 logger.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000242 for h in logger.handlers[:]:
243 logger.removeHandler(h)
244 logger.propagate = propagate
245 logger.disabled = 0
Łukasz Langa26d513c2010-11-10 18:57:39 +0000246 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000247 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000248 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000249 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000250 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000251 logger.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000252
253 #Disable any old loggers. There's no point deleting
254 #them as other threads may continue to hold references
255 #and by disabling them, you stop them doing any logging.
Christian Heimes96f31632007-11-12 01:32:03 +0000256 #However, don't disable children of named loggers, as that's
257 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000258 #for log in existing:
259 # logger = root.manager.loggerDict[log]
260 # if log in child_loggers:
261 # logger.level = logging.NOTSET
262 # logger.handlers = []
263 # logger.propagate = 1
264 # elif disable_existing_loggers:
265 # logger.disabled = 1
266 _handle_existing_loggers(existing, child_loggers, disable_existing)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000267
Xtreak087570a2018-07-02 14:27:46 +0530268
269def _clearExistingHandlers():
270 """Clear and close existing handlers"""
271 logging._handlers.clear()
272 logging.shutdown(logging._handlerList[:])
273 del logging._handlerList[:]
274
275
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000276IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
277
278
279def valid_ident(s):
280 m = IDENTIFIER.match(s)
281 if not m:
282 raise ValueError('Not a valid Python identifier: %r' % s)
283 return True
284
285
Vinay Sajipb1698d42014-03-20 13:14:39 +0000286class ConvertingMixin(object):
287 """For ConvertingXXX's, this mixin class provides common functions"""
288
289 def convert_with_key(self, key, value, replace=True):
290 result = self.configurator.convert(value)
291 #If the converted value is different, save for next time
292 if value is not result:
293 if replace:
294 self[key] = result
295 if type(result) in (ConvertingDict, ConvertingList,
296 ConvertingTuple):
297 result.parent = self
298 result.key = key
299 return result
300
301 def convert(self, value):
302 result = self.configurator.convert(value)
303 if value is not result:
304 if type(result) in (ConvertingDict, ConvertingList,
305 ConvertingTuple):
306 result.parent = self
307 return result
308
309
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000310# The ConvertingXXX classes are wrappers around standard Python containers,
311# and they serve to convert any suitable values in the container. The
312# conversion converts base dicts, lists and tuples to their wrapped
313# equivalents, whereas strings which match a conversion format are converted
314# appropriately.
315#
316# Each wrapper should have a configurator attribute holding the actual
317# configurator to use for conversion.
318
Vinay Sajipb1698d42014-03-20 13:14:39 +0000319class ConvertingDict(dict, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000320 """A converting dictionary wrapper."""
321
322 def __getitem__(self, key):
323 value = dict.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000324 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000325
326 def get(self, key, default=None):
327 value = dict.get(self, key, default)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000328 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000329
330 def pop(self, key, default=None):
331 value = dict.pop(self, key, default)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000332 return self.convert_with_key(key, value, replace=False)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000333
Vinay Sajipb1698d42014-03-20 13:14:39 +0000334class ConvertingList(list, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000335 """A converting list wrapper."""
336 def __getitem__(self, key):
337 value = list.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000338 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000339
340 def pop(self, idx=-1):
341 value = list.pop(self, idx)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000342 return self.convert(value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000343
Vinay Sajipb1698d42014-03-20 13:14:39 +0000344class ConvertingTuple(tuple, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000345 """A converting tuple wrapper."""
346 def __getitem__(self, key):
347 value = tuple.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000348 # Can't replace a tuple entry.
349 return self.convert_with_key(key, value, replace=False)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000350
351class BaseConfigurator(object):
352 """
353 The configurator base class which defines some useful defaults.
354 """
355
356 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
357
358 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
359 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
360 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
361 DIGIT_PATTERN = re.compile(r'^\d+$')
362
363 value_converters = {
364 'ext' : 'ext_convert',
365 'cfg' : 'cfg_convert',
366 }
367
368 # We might want to use a different one, e.g. importlib
Brett Cannonc2368502010-06-12 00:39:28 +0000369 importer = staticmethod(__import__)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000370
371 def __init__(self, config):
372 self.config = ConvertingDict(config)
373 self.config.configurator = self
374
375 def resolve(self, s):
376 """
377 Resolve strings to objects using standard import and attribute
378 syntax.
379 """
380 name = s.split('.')
381 used = name.pop(0)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000382 try:
383 found = self.importer(used)
384 for frag in name:
385 used += '.' + frag
386 try:
387 found = getattr(found, frag)
388 except AttributeError:
389 self.importer(used)
390 found = getattr(found, frag)
391 return found
392 except ImportError:
393 e, tb = sys.exc_info()[1:]
394 v = ValueError('Cannot resolve %r: %s' % (s, e))
395 v.__cause__, v.__traceback__ = e, tb
396 raise v
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000397
398 def ext_convert(self, value):
399 """Default converter for the ext:// protocol."""
400 return self.resolve(value)
401
402 def cfg_convert(self, value):
403 """Default converter for the cfg:// protocol."""
404 rest = value
405 m = self.WORD_PATTERN.match(rest)
406 if m is None:
407 raise ValueError("Unable to convert %r" % value)
408 else:
409 rest = rest[m.end():]
410 d = self.config[m.groups()[0]]
411 #print d, rest
412 while rest:
413 m = self.DOT_PATTERN.match(rest)
414 if m:
415 d = d[m.groups()[0]]
416 else:
417 m = self.INDEX_PATTERN.match(rest)
418 if m:
419 idx = m.groups()[0]
420 if not self.DIGIT_PATTERN.match(idx):
421 d = d[idx]
422 else:
423 try:
424 n = int(idx) # try as number first (most likely)
425 d = d[n]
426 except TypeError:
427 d = d[idx]
428 if m:
429 rest = rest[m.end():]
430 else:
431 raise ValueError('Unable to convert '
432 '%r at %r' % (value, rest))
433 #rest should be empty
434 return d
435
436 def convert(self, value):
437 """
438 Convert values to an appropriate type. dicts, lists and tuples are
439 replaced by their converting alternatives. Strings are checked to
440 see if they have a conversion format and are converted if they do.
441 """
442 if not isinstance(value, ConvertingDict) and isinstance(value, dict):
443 value = ConvertingDict(value)
444 value.configurator = self
445 elif not isinstance(value, ConvertingList) and isinstance(value, list):
446 value = ConvertingList(value)
447 value.configurator = self
448 elif not isinstance(value, ConvertingTuple) and\
449 isinstance(value, tuple):
450 value = ConvertingTuple(value)
451 value.configurator = self
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000452 elif isinstance(value, str): # str for py3k
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000453 m = self.CONVERT_PATTERN.match(value)
454 if m:
455 d = m.groupdict()
456 prefix = d['prefix']
457 converter = self.value_converters.get(prefix, None)
458 if converter:
459 suffix = d['suffix']
460 converter = getattr(self, converter)
461 value = converter(suffix)
462 return value
463
464 def configure_custom(self, config):
465 """Configure an object with a user-supplied factory."""
466 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200467 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000468 c = self.resolve(c)
469 props = config.pop('.', None)
470 # Check for valid identifiers
Serhiy Storchaka3f2e6f12018-02-26 16:50:11 +0200471 kwargs = {k: config[k] for k in config if valid_ident(k)}
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000472 result = c(**kwargs)
473 if props:
474 for name, value in props.items():
475 setattr(result, name, value)
476 return result
477
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000478 def as_tuple(self, value):
479 """Utility function which converts lists to tuples."""
480 if isinstance(value, list):
481 value = tuple(value)
482 return value
483
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000484class DictConfigurator(BaseConfigurator):
485 """
486 Configure logging using a dictionary-like object to describe the
487 configuration.
488 """
489
490 def configure(self):
491 """Do the configuration."""
492
493 config = self.config
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000494 if 'version' not in config:
495 raise ValueError("dictionary doesn't specify a version")
496 if config['version'] != 1:
497 raise ValueError("Unsupported version: %s" % config['version'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000498 incremental = config.pop('incremental', False)
499 EMPTY_DICT = {}
500 logging._acquireLock()
501 try:
502 if incremental:
503 handlers = config.get('handlers', EMPTY_DICT)
504 for name in handlers:
505 if name not in logging._handlers:
506 raise ValueError('No handler found with '
507 'name %r' % name)
508 else:
509 try:
510 handler = logging._handlers[name]
511 handler_config = handlers[name]
512 level = handler_config.get('level', None)
513 if level:
514 handler.setLevel(logging._checkLevel(level))
515 except Exception as e:
516 raise ValueError('Unable to configure handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100517 '%r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000518 loggers = config.get('loggers', EMPTY_DICT)
519 for name in loggers:
520 try:
521 self.configure_logger(name, loggers[name], True)
522 except Exception as e:
523 raise ValueError('Unable to configure logger '
Vinay Sajipaa275822016-10-03 19:45:50 +0100524 '%r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000525 root = config.get('root', None)
526 if root:
527 try:
528 self.configure_root(root, True)
529 except Exception as e:
530 raise ValueError('Unable to configure root '
Vinay Sajipaa275822016-10-03 19:45:50 +0100531 'logger') from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000532 else:
533 disable_existing = config.pop('disable_existing_loggers', True)
534
Xtreak087570a2018-07-02 14:27:46 +0530535 _clearExistingHandlers()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000536
537 # Do formatters first - they don't refer to anything else
538 formatters = config.get('formatters', EMPTY_DICT)
539 for name in formatters:
540 try:
541 formatters[name] = self.configure_formatter(
542 formatters[name])
543 except Exception as e:
544 raise ValueError('Unable to configure '
Vinay Sajipaa275822016-10-03 19:45:50 +0100545 'formatter %r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000546 # Next, do filters - they don't refer to anything else, either
547 filters = config.get('filters', EMPTY_DICT)
548 for name in filters:
549 try:
550 filters[name] = self.configure_filter(filters[name])
551 except Exception as e:
552 raise ValueError('Unable to configure '
Vinay Sajipaa275822016-10-03 19:45:50 +0100553 'filter %r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000554
555 # Next, do handlers - they refer to formatters and filters
556 # As handlers can refer to other handlers, sort the keys
557 # to allow a deterministic order of configuration
558 handlers = config.get('handlers', EMPTY_DICT)
Vinay Sajip3f885b52013-03-22 15:19:54 +0000559 deferred = []
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000560 for name in sorted(handlers):
561 try:
562 handler = self.configure_handler(handlers[name])
563 handler.name = name
564 handlers[name] = handler
565 except Exception as e:
Vinay Sajipb7403432016-10-03 19:50:56 +0100566 if 'target not configured yet' in str(e.__cause__):
Vinay Sajip3f885b52013-03-22 15:19:54 +0000567 deferred.append(name)
568 else:
569 raise ValueError('Unable to configure handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100570 '%r' % name) from e
Vinay Sajip3f885b52013-03-22 15:19:54 +0000571
572 # Now do any that were deferred
573 for name in deferred:
574 try:
575 handler = self.configure_handler(handlers[name])
576 handler.name = name
577 handlers[name] = handler
578 except Exception as e:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000579 raise ValueError('Unable to configure handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100580 '%r' % name) from e
Vinay Sajip3f885b52013-03-22 15:19:54 +0000581
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000582 # Next, do loggers - they refer to handlers and filters
583
584 #we don't want to lose the existing loggers,
585 #since other threads may have pointers to them.
586 #existing is set to contain all existing loggers,
587 #and as we go through the new configuration we
588 #remove any which are configured. At the end,
589 #what's left in existing is the set of loggers
590 #which were in the previous configuration but
591 #which are not in the new configuration.
592 root = logging.root
593 existing = list(root.manager.loggerDict.keys())
594 #The list needs to be sorted so that we can
595 #avoid disabling child loggers of explicitly
596 #named loggers. With a sorted list it is easier
597 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100598 existing.sort()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000599 #We'll keep the list of existing loggers
600 #which are children of named loggers here...
601 child_loggers = []
602 #now set up the new ones...
603 loggers = config.get('loggers', EMPTY_DICT)
604 for name in loggers:
605 if name in existing:
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000606 i = existing.index(name) + 1 # look after name
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000607 prefixed = name + "."
608 pflen = len(prefixed)
609 num_existing = len(existing)
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000610 while i < num_existing:
611 if existing[i][:pflen] == prefixed:
612 child_loggers.append(existing[i])
613 i += 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000614 existing.remove(name)
615 try:
616 self.configure_logger(name, loggers[name])
617 except Exception as e:
618 raise ValueError('Unable to configure logger '
Vinay Sajipaa275822016-10-03 19:45:50 +0100619 '%r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000620
621 #Disable any old loggers. There's no point deleting
622 #them as other threads may continue to hold references
623 #and by disabling them, you stop them doing any logging.
624 #However, don't disable children of named loggers, as that's
625 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000626 #for log in existing:
627 # logger = root.manager.loggerDict[log]
628 # if log in child_loggers:
629 # logger.level = logging.NOTSET
630 # logger.handlers = []
631 # logger.propagate = True
632 # elif disable_existing:
633 # logger.disabled = True
634 _handle_existing_loggers(existing, child_loggers,
635 disable_existing)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000636
637 # And finally, do the root logger
638 root = config.get('root', None)
639 if root:
640 try:
641 self.configure_root(root)
642 except Exception as e:
643 raise ValueError('Unable to configure root '
Vinay Sajipaa275822016-10-03 19:45:50 +0100644 'logger') from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000645 finally:
646 logging._releaseLock()
647
648 def configure_formatter(self, config):
649 """Configure a formatter from a dictionary."""
650 if '()' in config:
651 factory = config['()'] # for use in exception handler
652 try:
653 result = self.configure_custom(config)
654 except TypeError as te:
655 if "'format'" not in str(te):
656 raise
657 #Name of parameter changed from fmt to format.
658 #Retry with old name.
659 #This is so that code can be used with older Python versions
660 #(e.g. by Django)
661 config['fmt'] = config.pop('format')
662 config['()'] = factory
663 result = self.configure_custom(config)
664 else:
665 fmt = config.get('format', None)
666 dfmt = config.get('datefmt', None)
Vinay Sajip28421c62013-03-29 17:56:54 +0000667 style = config.get('style', '%')
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100668 cname = config.get('class', None)
669 if not cname:
670 c = logging.Formatter
671 else:
672 c = _resolve(cname)
673 result = c(fmt, dfmt, style)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000674 return result
675
676 def configure_filter(self, config):
677 """Configure a filter from a dictionary."""
678 if '()' in config:
679 result = self.configure_custom(config)
680 else:
681 name = config.get('name', '')
682 result = logging.Filter(name)
683 return result
684
685 def add_filters(self, filterer, filters):
686 """Add filters to a filterer from a list of names."""
687 for f in filters:
688 try:
689 filterer.addFilter(self.config['filters'][f])
690 except Exception as e:
Vinay Sajipaa275822016-10-03 19:45:50 +0100691 raise ValueError('Unable to add filter %r' % f) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000692
693 def configure_handler(self, config):
694 """Configure a handler from a dictionary."""
Vinay Sajip28421c62013-03-29 17:56:54 +0000695 config_copy = dict(config) # for restoring in case of error
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000696 formatter = config.pop('formatter', None)
697 if formatter:
698 try:
699 formatter = self.config['formatters'][formatter]
700 except Exception as e:
701 raise ValueError('Unable to set formatter '
Vinay Sajipaa275822016-10-03 19:45:50 +0100702 '%r' % formatter) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000703 level = config.pop('level', None)
704 filters = config.pop('filters', None)
705 if '()' in config:
706 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200707 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000708 c = self.resolve(c)
709 factory = c
710 else:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000711 cname = config.pop('class')
712 klass = self.resolve(cname)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000713 #Special case for handler which refers to another handler
714 if issubclass(klass, logging.handlers.MemoryHandler) and\
715 'target' in config:
716 try:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000717 th = self.config['handlers'][config['target']]
718 if not isinstance(th, logging.Handler):
Vinay Sajip28421c62013-03-29 17:56:54 +0000719 config.update(config_copy) # restore for deferred cfg
Vinay Sajip3f885b52013-03-22 15:19:54 +0000720 raise TypeError('target not configured yet')
721 config['target'] = th
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000722 except Exception as e:
723 raise ValueError('Unable to set target handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100724 '%r' % config['target']) from e
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000725 elif issubclass(klass, logging.handlers.SMTPHandler) and\
726 'mailhost' in config:
727 config['mailhost'] = self.as_tuple(config['mailhost'])
728 elif issubclass(klass, logging.handlers.SysLogHandler) and\
729 'address' in config:
730 config['address'] = self.as_tuple(config['address'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000731 factory = klass
Vinay Sajip8d270232012-11-15 14:20:18 +0000732 props = config.pop('.', None)
Serhiy Storchaka3f2e6f12018-02-26 16:50:11 +0200733 kwargs = {k: config[k] for k in config if valid_ident(k)}
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000734 try:
735 result = factory(**kwargs)
736 except TypeError as te:
737 if "'stream'" not in str(te):
738 raise
739 #The argument name changed from strm to stream
740 #Retry with old name.
741 #This is so that code can be used with older Python versions
742 #(e.g. by Django)
743 kwargs['strm'] = kwargs.pop('stream')
744 result = factory(**kwargs)
745 if formatter:
746 result.setFormatter(formatter)
747 if level is not None:
748 result.setLevel(logging._checkLevel(level))
749 if filters:
750 self.add_filters(result, filters)
Vinay Sajip8d270232012-11-15 14:20:18 +0000751 if props:
752 for name, value in props.items():
753 setattr(result, name, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000754 return result
755
756 def add_handlers(self, logger, handlers):
757 """Add handlers to a logger from a list of names."""
758 for h in handlers:
759 try:
760 logger.addHandler(self.config['handlers'][h])
761 except Exception as e:
Vinay Sajipaa275822016-10-03 19:45:50 +0100762 raise ValueError('Unable to add handler %r' % h) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000763
764 def common_logger_config(self, logger, config, incremental=False):
765 """
766 Perform configuration which is common to root and non-root loggers.
767 """
768 level = config.get('level', None)
769 if level is not None:
770 logger.setLevel(logging._checkLevel(level))
771 if not incremental:
772 #Remove any existing handlers
773 for h in logger.handlers[:]:
774 logger.removeHandler(h)
775 handlers = config.get('handlers', None)
776 if handlers:
777 self.add_handlers(logger, handlers)
778 filters = config.get('filters', None)
779 if filters:
780 self.add_filters(logger, filters)
781
782 def configure_logger(self, name, config, incremental=False):
783 """Configure a non-root logger from a dictionary."""
784 logger = logging.getLogger(name)
785 self.common_logger_config(logger, config, incremental)
786 propagate = config.get('propagate', None)
787 if propagate is not None:
788 logger.propagate = propagate
789
790 def configure_root(self, config, incremental=False):
791 """Configure a root logger from a dictionary."""
792 root = logging.getLogger()
793 self.common_logger_config(root, config, incremental)
794
795dictConfigClass = DictConfigurator
796
797def dictConfig(config):
798 """Configure logging using a dictionary."""
799 dictConfigClass(config).configure()
800
801
Vinay Sajip4ded5512012-10-02 15:56:16 +0100802def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000803 """
804 Start up a socket server on the specified port, and listen for new
805 configurations.
806
807 These will be sent as a file suitable for processing by fileConfig().
808 Returns a Thread object on which you can call start() to start the server,
809 and which you can join() when appropriate. To stop the server, call
810 stopListening().
Vinay Sajip3e763da2012-10-02 16:15:33 +0100811
812 Use the ``verify`` argument to verify any bytes received across the wire
813 from a client. If specified, it should be a callable which receives a
814 single argument - the bytes of configuration data received across the
815 network - and it should return either ``None``, to indicate that the
816 passed in bytes could not be verified and should be discarded, or a
817 byte string which is then passed to the configuration machinery as
818 normal. Note that you can return transformed bytes, e.g. by decrypting
819 the bytes passed in.
Guido van Rossum57102f82002-11-13 16:15:58 +0000820 """
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()
Victor Stinner97d7e652017-09-13 01:44:08 -0700897 self.server_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()