blob: cfd93116eeddbd88fe48cd91cae8a6c4e3558341 [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)
BNMetrics18fb1fb2018-10-15 19:41:36 +0100669
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100670 if not cname:
671 c = logging.Formatter
672 else:
673 c = _resolve(cname)
BNMetrics18fb1fb2018-10-15 19:41:36 +0100674
675 # A TypeError would be raised if "validate" key is passed in with a formatter callable
676 # that does not accept "validate" as a parameter
677 if 'validate' in config: # if user hasn't mentioned it, the default will be fine
678 result = c(fmt, dfmt, style, config['validate'])
679 else:
680 result = c(fmt, dfmt, style)
681
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000682 return result
683
684 def configure_filter(self, config):
685 """Configure a filter from a dictionary."""
686 if '()' in config:
687 result = self.configure_custom(config)
688 else:
689 name = config.get('name', '')
690 result = logging.Filter(name)
691 return result
692
693 def add_filters(self, filterer, filters):
694 """Add filters to a filterer from a list of names."""
695 for f in filters:
696 try:
697 filterer.addFilter(self.config['filters'][f])
698 except Exception as e:
Vinay Sajipaa275822016-10-03 19:45:50 +0100699 raise ValueError('Unable to add filter %r' % f) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000700
701 def configure_handler(self, config):
702 """Configure a handler from a dictionary."""
Vinay Sajip28421c62013-03-29 17:56:54 +0000703 config_copy = dict(config) # for restoring in case of error
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000704 formatter = config.pop('formatter', None)
705 if formatter:
706 try:
707 formatter = self.config['formatters'][formatter]
708 except Exception as e:
709 raise ValueError('Unable to set formatter '
Vinay Sajipaa275822016-10-03 19:45:50 +0100710 '%r' % formatter) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000711 level = config.pop('level', None)
712 filters = config.pop('filters', None)
713 if '()' in config:
714 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200715 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000716 c = self.resolve(c)
717 factory = c
718 else:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000719 cname = config.pop('class')
720 klass = self.resolve(cname)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000721 #Special case for handler which refers to another handler
722 if issubclass(klass, logging.handlers.MemoryHandler) and\
723 'target' in config:
724 try:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000725 th = self.config['handlers'][config['target']]
726 if not isinstance(th, logging.Handler):
Vinay Sajip28421c62013-03-29 17:56:54 +0000727 config.update(config_copy) # restore for deferred cfg
Vinay Sajip3f885b52013-03-22 15:19:54 +0000728 raise TypeError('target not configured yet')
729 config['target'] = th
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000730 except Exception as e:
731 raise ValueError('Unable to set target handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100732 '%r' % config['target']) from e
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000733 elif issubclass(klass, logging.handlers.SMTPHandler) and\
734 'mailhost' in config:
735 config['mailhost'] = self.as_tuple(config['mailhost'])
736 elif issubclass(klass, logging.handlers.SysLogHandler) and\
737 'address' in config:
738 config['address'] = self.as_tuple(config['address'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000739 factory = klass
Vinay Sajip8d270232012-11-15 14:20:18 +0000740 props = config.pop('.', None)
Serhiy Storchaka3f2e6f12018-02-26 16:50:11 +0200741 kwargs = {k: config[k] for k in config if valid_ident(k)}
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000742 try:
743 result = factory(**kwargs)
744 except TypeError as te:
745 if "'stream'" not in str(te):
746 raise
747 #The argument name changed from strm to stream
748 #Retry with old name.
749 #This is so that code can be used with older Python versions
750 #(e.g. by Django)
751 kwargs['strm'] = kwargs.pop('stream')
752 result = factory(**kwargs)
753 if formatter:
754 result.setFormatter(formatter)
755 if level is not None:
756 result.setLevel(logging._checkLevel(level))
757 if filters:
758 self.add_filters(result, filters)
Vinay Sajip8d270232012-11-15 14:20:18 +0000759 if props:
760 for name, value in props.items():
761 setattr(result, name, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000762 return result
763
764 def add_handlers(self, logger, handlers):
765 """Add handlers to a logger from a list of names."""
766 for h in handlers:
767 try:
768 logger.addHandler(self.config['handlers'][h])
769 except Exception as e:
Vinay Sajipaa275822016-10-03 19:45:50 +0100770 raise ValueError('Unable to add handler %r' % h) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000771
772 def common_logger_config(self, logger, config, incremental=False):
773 """
774 Perform configuration which is common to root and non-root loggers.
775 """
776 level = config.get('level', None)
777 if level is not None:
778 logger.setLevel(logging._checkLevel(level))
779 if not incremental:
780 #Remove any existing handlers
781 for h in logger.handlers[:]:
782 logger.removeHandler(h)
783 handlers = config.get('handlers', None)
784 if handlers:
785 self.add_handlers(logger, handlers)
786 filters = config.get('filters', None)
787 if filters:
788 self.add_filters(logger, filters)
789
790 def configure_logger(self, name, config, incremental=False):
791 """Configure a non-root logger from a dictionary."""
792 logger = logging.getLogger(name)
793 self.common_logger_config(logger, config, incremental)
794 propagate = config.get('propagate', None)
795 if propagate is not None:
796 logger.propagate = propagate
797
798 def configure_root(self, config, incremental=False):
799 """Configure a root logger from a dictionary."""
800 root = logging.getLogger()
801 self.common_logger_config(root, config, incremental)
802
803dictConfigClass = DictConfigurator
804
805def dictConfig(config):
806 """Configure logging using a dictionary."""
807 dictConfigClass(config).configure()
808
809
Vinay Sajip4ded5512012-10-02 15:56:16 +0100810def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000811 """
812 Start up a socket server on the specified port, and listen for new
813 configurations.
814
815 These will be sent as a file suitable for processing by fileConfig().
816 Returns a Thread object on which you can call start() to start the server,
817 and which you can join() when appropriate. To stop the server, call
818 stopListening().
Vinay Sajip3e763da2012-10-02 16:15:33 +0100819
820 Use the ``verify`` argument to verify any bytes received across the wire
821 from a client. If specified, it should be a callable which receives a
822 single argument - the bytes of configuration data received across the
823 network - and it should return either ``None``, to indicate that the
824 passed in bytes could not be verified and should be discarded, or a
825 byte string which is then passed to the configuration machinery as
826 normal. Note that you can return transformed bytes, e.g. by decrypting
827 the bytes passed in.
Guido van Rossum57102f82002-11-13 16:15:58 +0000828 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000829
830 class ConfigStreamHandler(StreamRequestHandler):
831 """
832 Handler for a logging configuration request.
833
834 It expects a completely new logging configuration and uses fileConfig
835 to install it.
836 """
837 def handle(self):
838 """
839 Handle a request.
840
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000841 Each request is expected to be a 4-byte length, packed using
842 struct.pack(">L", n), followed by the config file.
843 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000844 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000845 try:
846 conn = self.connection
847 chunk = conn.recv(4)
848 if len(chunk) == 4:
849 slen = struct.unpack(">L", chunk)[0]
850 chunk = self.connection.recv(slen)
851 while len(chunk) < slen:
852 chunk = chunk + conn.recv(slen - len(chunk))
Vinay Sajip4ded5512012-10-02 15:56:16 +0100853 if self.server.verify is not None:
854 chunk = self.server.verify(chunk)
855 if chunk is not None: # verified, can process
856 chunk = chunk.decode("utf-8")
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000857 try:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100858 import json
859 d =json.loads(chunk)
860 assert isinstance(d, dict)
861 dictConfig(d)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100862 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100863 #Apply new configuration.
864
865 file = io.StringIO(chunk)
866 try:
867 fileConfig(file)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100868 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100869 traceback.print_exc()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000870 if self.server.ready:
871 self.server.ready.set()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200872 except OSError as e:
Vinay Sajip71dcb282014-03-20 13:03:17 +0000873 if e.errno != RESET_ERROR:
Guido van Rossum57102f82002-11-13 16:15:58 +0000874 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000875
876 class ConfigSocketReceiver(ThreadingTCPServer):
877 """
878 A simple TCP socket-based logging config receiver.
879 """
880
881 allow_reuse_address = 1
882
883 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100884 handler=None, ready=None, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000885 ThreadingTCPServer.__init__(self, (host, port), handler)
886 logging._acquireLock()
887 self.abort = 0
888 logging._releaseLock()
889 self.timeout = 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000890 self.ready = ready
Vinay Sajip4ded5512012-10-02 15:56:16 +0100891 self.verify = verify
Guido van Rossum57102f82002-11-13 16:15:58 +0000892
893 def serve_until_stopped(self):
894 import select
895 abort = 0
896 while not abort:
897 rd, wr, ex = select.select([self.socket.fileno()],
898 [], [],
899 self.timeout)
900 if rd:
901 self.handle_request()
902 logging._acquireLock()
903 abort = self.abort
904 logging._releaseLock()
Victor Stinner97d7e652017-09-13 01:44:08 -0700905 self.server_close()
Guido van Rossum57102f82002-11-13 16:15:58 +0000906
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000907 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000908
Vinay Sajip4ded5512012-10-02 15:56:16 +0100909 def __init__(self, rcvr, hdlr, port, verify):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000910 super(Server, self).__init__()
911 self.rcvr = rcvr
912 self.hdlr = hdlr
913 self.port = port
Vinay Sajip4ded5512012-10-02 15:56:16 +0100914 self.verify = verify
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000915 self.ready = threading.Event()
916
917 def run(self):
918 server = self.rcvr(port=self.port, handler=self.hdlr,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100919 ready=self.ready,
920 verify=self.verify)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000921 if self.port == 0:
922 self.port = server.server_address[1]
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000923 self.ready.set()
924 global _listener
925 logging._acquireLock()
926 _listener = server
927 logging._releaseLock()
928 server.serve_until_stopped()
929
Vinay Sajip4ded5512012-10-02 15:56:16 +0100930 return Server(ConfigSocketReceiver, ConfigStreamHandler, port, verify)
Guido van Rossum57102f82002-11-13 16:15:58 +0000931
932def stopListening():
933 """
934 Stop the listening server which was created with a call to listen().
935 """
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000936 global _listener
Vinay Sajip9fdd11b2010-09-25 17:48:25 +0000937 logging._acquireLock()
938 try:
939 if _listener:
940 _listener.abort = 1
941 _listener = None
942 finally:
Guido van Rossum57102f82002-11-13 16:15:58 +0000943 logging._releaseLock()