blob: 3bc63b78621abae6b697f4f0d5b122ae5750a312 [file] [log] [blame]
Vinay Sajip1d094af2019-09-22 03:51:51 +01001# Copyright 2001-2019 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 Sajip1d094af2019-09-22 03:51:51 +010022Copyright (C) 2001-2019 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
Inada Naokic2b7a662021-04-13 18:17:03 +090051def fileConfig(fname, defaults=None, disable_existing_loggers=True, encoding=None):
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:
Inada Naokic2b7a662021-04-13 18:17:03 +090069 encoding = io.text_encoding(encoding)
70 cp.read(fname, encoding=encoding)
Vinay Sajip989b69a2006-01-16 21:28:37 +000071
72 formatters = _create_formatters(cp)
73
74 # critical section
Guido van Rossum57102f82002-11-13 16:15:58 +000075 logging._acquireLock()
76 try:
Xtreak087570a2018-07-02 14:27:46 +053077 _clearExistingHandlers()
78
Vinay Sajip989b69a2006-01-16 21:28:37 +000079 # Handlers add themselves to logging._handlers
80 handlers = _install_handlers(cp, formatters)
Benjamin Petersonfea6a942008-07-02 16:11:42 +000081 _install_loggers(cp, handlers, disable_existing_loggers)
Guido van Rossum57102f82002-11-13 16:15:58 +000082 finally:
83 logging._releaseLock()
84
Vinay Sajip989b69a2006-01-16 21:28:37 +000085
Vinay Sajip7a7160b2006-01-20 18:28:03 +000086def _resolve(name):
87 """Resolve a dotted name to a global object."""
Neal Norwitz9d72bb42007-04-17 08:48:32 +000088 name = name.split('.')
Vinay Sajip7a7160b2006-01-20 18:28:03 +000089 used = name.pop(0)
90 found = __import__(used)
91 for n in name:
92 used = used + '.' + n
93 try:
94 found = getattr(found, n)
95 except AttributeError:
96 __import__(used)
97 found = getattr(found, n)
98 return found
99
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000100def _strip_spaces(alist):
sanjayp2ae4ad72017-11-15 14:58:11 +0530101 return map(str.strip, alist)
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000102
Vinay Sajip989b69a2006-01-16 21:28:37 +0000103def _create_formatters(cp):
104 """Create and return formatters"""
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000105 flist = cp["formatters"]["keys"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000106 if not len(flist):
107 return {}
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000108 flist = flist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000109 flist = _strip_spaces(flist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000110 formatters = {}
111 for form in flist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000112 sectname = "formatter_%s" % form
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000113 fs = cp.get(sectname, "format", raw=True, fallback=None)
114 dfs = cp.get(sectname, "datefmt", raw=True, fallback=None)
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100115 stl = cp.get(sectname, "style", raw=True, fallback='%')
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000116 c = logging.Formatter
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000117 class_name = cp[sectname].get("class")
118 if class_name:
119 c = _resolve(class_name)
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100120 f = c(fs, dfs, stl)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000121 formatters[form] = f
122 return formatters
123
124
125def _install_handlers(cp, formatters):
126 """Install and return handlers"""
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000127 hlist = cp["handlers"]["keys"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000128 if not len(hlist):
129 return {}
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000130 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000131 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000132 handlers = {}
133 fixups = [] #for inter-handler references
134 for hand in hlist:
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000135 section = cp["handler_%s" % hand]
136 klass = section["class"]
137 fmt = section.get("formatter", "")
Georg Brandl3dbca812008-07-23 16:10:53 +0000138 try:
139 klass = eval(klass, vars(logging))
140 except (AttributeError, NameError):
141 klass = _resolve(klass)
Preston Landers6ea56d22017-08-02 15:44:28 -0500142 args = section.get("args", '()')
Vinay Sajip989b69a2006-01-16 21:28:37 +0000143 args = eval(args, vars(logging))
Preston Landers6ea56d22017-08-02 15:44:28 -0500144 kwargs = section.get("kwargs", '{}')
145 kwargs = eval(kwargs, vars(logging))
146 h = klass(*args, **kwargs)
Lucas Cimonb15100f2019-10-31 09:06:25 +0100147 h.name = hand
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000148 if "level" in section:
149 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700150 h.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000151 if len(fmt):
152 h.setFormatter(formatters[fmt])
Benjamin Peterson41181742008-07-02 20:22:54 +0000153 if issubclass(klass, logging.handlers.MemoryHandler):
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000154 target = section.get("target", "")
Vinay Sajip989b69a2006-01-16 21:28:37 +0000155 if len(target): #the target handler may not be loaded yet, so keep for later...
156 fixups.append((h, target))
157 handlers[hand] = h
158 #now all handlers are loaded, fixup inter-handler references...
159 for h, t in fixups:
160 h.setTarget(handlers[t])
161 return handlers
162
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000163def _handle_existing_loggers(existing, child_loggers, disable_existing):
164 """
165 When (re)configuring logging, handle loggers which were in the previous
166 configuration but are not in the new configuration. There's no point
167 deleting them as other threads may continue to hold references to them;
168 and by disabling them, you stop them doing any logging.
Vinay Sajip989b69a2006-01-16 21:28:37 +0000169
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000170 However, don't disable children of named loggers, as that's probably not
171 what was intended by the user. Also, allow existing loggers to NOT be
172 disabled if disable_existing is false.
173 """
174 root = logging.root
175 for log in existing:
176 logger = root.manager.loggerDict[log]
177 if log in child_loggers:
Vinay Sajip1d094af2019-09-22 03:51:51 +0100178 if not isinstance(logger, logging.PlaceHolder):
179 logger.setLevel(logging.NOTSET)
180 logger.handlers = []
181 logger.propagate = True
Vinay Sajip68b4cc82013-03-23 11:18:45 +0000182 else:
183 logger.disabled = disable_existing
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000184
185def _install_loggers(cp, handlers, disable_existing):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000186 """Create and install loggers"""
187
188 # configure the root first
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000189 llist = cp["loggers"]["keys"]
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000190 llist = llist.split(",")
sanjayp2ae4ad72017-11-15 14:58:11 +0530191 llist = list(_strip_spaces(llist))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000192 llist.remove("root")
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000193 section = cp["logger_root"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000194 root = logging.root
195 log = root
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000196 if "level" in section:
197 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700198 log.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000199 for h in root.handlers[:]:
200 root.removeHandler(h)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000201 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000202 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000203 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000204 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000205 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000206 log.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000207
208 #and now the others...
209 #we don't want to lose the existing loggers,
210 #since other threads may have pointers to them.
211 #existing is set to contain all existing loggers,
212 #and as we go through the new configuration we
213 #remove any which are configured. At the end,
214 #what's left in existing is the set of loggers
215 #which were in the previous configuration but
216 #which are not in the new configuration.
Guido van Rossum8b8a5432007-02-12 00:07:01 +0000217 existing = list(root.manager.loggerDict.keys())
Christian Heimes96f31632007-11-12 01:32:03 +0000218 #The list needs to be sorted so that we can
219 #avoid disabling child loggers of explicitly
220 #named loggers. With a sorted list it is easier
221 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100222 existing.sort()
Christian Heimes96f31632007-11-12 01:32:03 +0000223 #We'll keep the list of existing loggers
224 #which are children of named loggers here...
225 child_loggers = []
Vinay Sajip989b69a2006-01-16 21:28:37 +0000226 #now set up the new ones...
227 for log in llist:
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000228 section = cp["logger_%s" % log]
229 qn = section["qualname"]
230 propagate = section.getint("propagate", fallback=1)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000231 logger = logging.getLogger(qn)
232 if qn in existing:
Vinay Sajip3f84b072011-03-07 17:49:33 +0000233 i = existing.index(qn) + 1 # start with the entry after qn
Christian Heimes96f31632007-11-12 01:32:03 +0000234 prefixed = qn + "."
235 pflen = len(prefixed)
236 num_existing = len(existing)
Vinay Sajip3f84b072011-03-07 17:49:33 +0000237 while i < num_existing:
238 if existing[i][:pflen] == prefixed:
239 child_loggers.append(existing[i])
240 i += 1
Vinay Sajip989b69a2006-01-16 21:28:37 +0000241 existing.remove(qn)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000242 if "level" in section:
243 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700244 logger.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000245 for h in logger.handlers[:]:
246 logger.removeHandler(h)
247 logger.propagate = propagate
248 logger.disabled = 0
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000249 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000250 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000251 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000252 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000253 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000254 logger.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000255
256 #Disable any old loggers. There's no point deleting
257 #them as other threads may continue to hold references
258 #and by disabling them, you stop them doing any logging.
Christian Heimes96f31632007-11-12 01:32:03 +0000259 #However, don't disable children of named loggers, as that's
260 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000261 #for log in existing:
262 # logger = root.manager.loggerDict[log]
263 # if log in child_loggers:
264 # logger.level = logging.NOTSET
265 # logger.handlers = []
266 # logger.propagate = 1
267 # elif disable_existing_loggers:
268 # logger.disabled = 1
269 _handle_existing_loggers(existing, child_loggers, disable_existing)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000270
Xtreak087570a2018-07-02 14:27:46 +0530271
272def _clearExistingHandlers():
273 """Clear and close existing handlers"""
274 logging._handlers.clear()
275 logging.shutdown(logging._handlerList[:])
276 del logging._handlerList[:]
277
278
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000279IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
280
281
282def valid_ident(s):
283 m = IDENTIFIER.match(s)
284 if not m:
285 raise ValueError('Not a valid Python identifier: %r' % s)
286 return True
287
288
Vinay Sajipb1698d42014-03-20 13:14:39 +0000289class ConvertingMixin(object):
290 """For ConvertingXXX's, this mixin class provides common functions"""
291
292 def convert_with_key(self, key, value, replace=True):
293 result = self.configurator.convert(value)
294 #If the converted value is different, save for next time
295 if value is not result:
296 if replace:
297 self[key] = result
298 if type(result) in (ConvertingDict, ConvertingList,
299 ConvertingTuple):
300 result.parent = self
301 result.key = key
302 return result
303
304 def convert(self, value):
305 result = self.configurator.convert(value)
306 if value is not result:
307 if type(result) in (ConvertingDict, ConvertingList,
308 ConvertingTuple):
309 result.parent = self
310 return result
311
312
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000313# The ConvertingXXX classes are wrappers around standard Python containers,
314# and they serve to convert any suitable values in the container. The
315# conversion converts base dicts, lists and tuples to their wrapped
316# equivalents, whereas strings which match a conversion format are converted
317# appropriately.
318#
319# Each wrapper should have a configurator attribute holding the actual
320# configurator to use for conversion.
321
Vinay Sajipb1698d42014-03-20 13:14:39 +0000322class ConvertingDict(dict, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000323 """A converting dictionary wrapper."""
324
325 def __getitem__(self, key):
326 value = dict.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000327 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000328
329 def get(self, key, default=None):
330 value = dict.get(self, key, default)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000331 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000332
333 def pop(self, key, default=None):
334 value = dict.pop(self, key, default)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000335 return self.convert_with_key(key, value, replace=False)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000336
Vinay Sajipb1698d42014-03-20 13:14:39 +0000337class ConvertingList(list, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000338 """A converting list wrapper."""
339 def __getitem__(self, key):
340 value = list.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000341 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000342
343 def pop(self, idx=-1):
344 value = list.pop(self, idx)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000345 return self.convert(value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000346
Vinay Sajipb1698d42014-03-20 13:14:39 +0000347class ConvertingTuple(tuple, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000348 """A converting tuple wrapper."""
349 def __getitem__(self, key):
350 value = tuple.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000351 # Can't replace a tuple entry.
352 return self.convert_with_key(key, value, replace=False)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000353
354class BaseConfigurator(object):
355 """
356 The configurator base class which defines some useful defaults.
357 """
358
359 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
360
361 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
362 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
363 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
364 DIGIT_PATTERN = re.compile(r'^\d+$')
365
366 value_converters = {
367 'ext' : 'ext_convert',
368 'cfg' : 'cfg_convert',
369 }
370
371 # We might want to use a different one, e.g. importlib
Brett Cannonc2368502010-06-12 00:39:28 +0000372 importer = staticmethod(__import__)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000373
374 def __init__(self, config):
375 self.config = ConvertingDict(config)
376 self.config.configurator = self
377
378 def resolve(self, s):
379 """
380 Resolve strings to objects using standard import and attribute
381 syntax.
382 """
383 name = s.split('.')
384 used = name.pop(0)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000385 try:
386 found = self.importer(used)
387 for frag in name:
388 used += '.' + frag
389 try:
390 found = getattr(found, frag)
391 except AttributeError:
392 self.importer(used)
393 found = getattr(found, frag)
394 return found
395 except ImportError:
396 e, tb = sys.exc_info()[1:]
397 v = ValueError('Cannot resolve %r: %s' % (s, e))
398 v.__cause__, v.__traceback__ = e, tb
399 raise v
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000400
401 def ext_convert(self, value):
402 """Default converter for the ext:// protocol."""
403 return self.resolve(value)
404
405 def cfg_convert(self, value):
406 """Default converter for the cfg:// protocol."""
407 rest = value
408 m = self.WORD_PATTERN.match(rest)
409 if m is None:
410 raise ValueError("Unable to convert %r" % value)
411 else:
412 rest = rest[m.end():]
413 d = self.config[m.groups()[0]]
414 #print d, rest
415 while rest:
416 m = self.DOT_PATTERN.match(rest)
417 if m:
418 d = d[m.groups()[0]]
419 else:
420 m = self.INDEX_PATTERN.match(rest)
421 if m:
422 idx = m.groups()[0]
423 if not self.DIGIT_PATTERN.match(idx):
424 d = d[idx]
425 else:
426 try:
427 n = int(idx) # try as number first (most likely)
428 d = d[n]
429 except TypeError:
430 d = d[idx]
431 if m:
432 rest = rest[m.end():]
433 else:
434 raise ValueError('Unable to convert '
435 '%r at %r' % (value, rest))
436 #rest should be empty
437 return d
438
439 def convert(self, value):
440 """
441 Convert values to an appropriate type. dicts, lists and tuples are
442 replaced by their converting alternatives. Strings are checked to
443 see if they have a conversion format and are converted if they do.
444 """
445 if not isinstance(value, ConvertingDict) and isinstance(value, dict):
446 value = ConvertingDict(value)
447 value.configurator = self
448 elif not isinstance(value, ConvertingList) and isinstance(value, list):
449 value = ConvertingList(value)
450 value.configurator = self
451 elif not isinstance(value, ConvertingTuple) and\
Vinay Sajip46abfc12020-01-01 19:32:11 +0000452 isinstance(value, tuple) and not hasattr(value, '_fields'):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000453 value = ConvertingTuple(value)
454 value.configurator = self
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000455 elif isinstance(value, str): # str for py3k
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000456 m = self.CONVERT_PATTERN.match(value)
457 if m:
458 d = m.groupdict()
459 prefix = d['prefix']
460 converter = self.value_converters.get(prefix, None)
461 if converter:
462 suffix = d['suffix']
463 converter = getattr(self, converter)
464 value = converter(suffix)
465 return value
466
467 def configure_custom(self, config):
468 """Configure an object with a user-supplied factory."""
469 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200470 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000471 c = self.resolve(c)
472 props = config.pop('.', None)
473 # Check for valid identifiers
Serhiy Storchaka3f2e6f12018-02-26 16:50:11 +0200474 kwargs = {k: config[k] for k in config if valid_ident(k)}
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000475 result = c(**kwargs)
476 if props:
477 for name, value in props.items():
478 setattr(result, name, value)
479 return result
480
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000481 def as_tuple(self, value):
482 """Utility function which converts lists to tuples."""
483 if isinstance(value, list):
484 value = tuple(value)
485 return value
486
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000487class DictConfigurator(BaseConfigurator):
488 """
489 Configure logging using a dictionary-like object to describe the
490 configuration.
491 """
492
493 def configure(self):
494 """Do the configuration."""
495
496 config = self.config
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000497 if 'version' not in config:
498 raise ValueError("dictionary doesn't specify a version")
499 if config['version'] != 1:
500 raise ValueError("Unsupported version: %s" % config['version'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000501 incremental = config.pop('incremental', False)
502 EMPTY_DICT = {}
503 logging._acquireLock()
504 try:
505 if incremental:
506 handlers = config.get('handlers', EMPTY_DICT)
507 for name in handlers:
508 if name not in logging._handlers:
509 raise ValueError('No handler found with '
510 'name %r' % name)
511 else:
512 try:
513 handler = logging._handlers[name]
514 handler_config = handlers[name]
515 level = handler_config.get('level', None)
516 if level:
517 handler.setLevel(logging._checkLevel(level))
518 except Exception as e:
519 raise ValueError('Unable to configure handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100520 '%r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000521 loggers = config.get('loggers', EMPTY_DICT)
522 for name in loggers:
523 try:
524 self.configure_logger(name, loggers[name], True)
525 except Exception as e:
526 raise ValueError('Unable to configure logger '
Vinay Sajipaa275822016-10-03 19:45:50 +0100527 '%r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000528 root = config.get('root', None)
529 if root:
530 try:
531 self.configure_root(root, True)
532 except Exception as e:
533 raise ValueError('Unable to configure root '
Vinay Sajipaa275822016-10-03 19:45:50 +0100534 'logger') from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000535 else:
536 disable_existing = config.pop('disable_existing_loggers', True)
537
Xtreak087570a2018-07-02 14:27:46 +0530538 _clearExistingHandlers()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000539
540 # Do formatters first - they don't refer to anything else
541 formatters = config.get('formatters', EMPTY_DICT)
542 for name in formatters:
543 try:
544 formatters[name] = self.configure_formatter(
545 formatters[name])
546 except Exception as e:
547 raise ValueError('Unable to configure '
Vinay Sajipaa275822016-10-03 19:45:50 +0100548 'formatter %r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000549 # Next, do filters - they don't refer to anything else, either
550 filters = config.get('filters', EMPTY_DICT)
551 for name in filters:
552 try:
553 filters[name] = self.configure_filter(filters[name])
554 except Exception as e:
555 raise ValueError('Unable to configure '
Vinay Sajipaa275822016-10-03 19:45:50 +0100556 'filter %r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000557
558 # Next, do handlers - they refer to formatters and filters
559 # As handlers can refer to other handlers, sort the keys
560 # to allow a deterministic order of configuration
561 handlers = config.get('handlers', EMPTY_DICT)
Vinay Sajip3f885b52013-03-22 15:19:54 +0000562 deferred = []
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000563 for name in sorted(handlers):
564 try:
565 handler = self.configure_handler(handlers[name])
566 handler.name = name
567 handlers[name] = handler
568 except Exception as e:
Vinay Sajipb7403432016-10-03 19:50:56 +0100569 if 'target not configured yet' in str(e.__cause__):
Vinay Sajip3f885b52013-03-22 15:19:54 +0000570 deferred.append(name)
571 else:
572 raise ValueError('Unable to configure handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100573 '%r' % name) from e
Vinay Sajip3f885b52013-03-22 15:19:54 +0000574
575 # Now do any that were deferred
576 for name in deferred:
577 try:
578 handler = self.configure_handler(handlers[name])
579 handler.name = name
580 handlers[name] = handler
581 except Exception as e:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000582 raise ValueError('Unable to configure handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100583 '%r' % name) from e
Vinay Sajip3f885b52013-03-22 15:19:54 +0000584
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000585 # Next, do loggers - they refer to handlers and filters
586
587 #we don't want to lose the existing loggers,
588 #since other threads may have pointers to them.
589 #existing is set to contain all existing loggers,
590 #and as we go through the new configuration we
591 #remove any which are configured. At the end,
592 #what's left in existing is the set of loggers
593 #which were in the previous configuration but
594 #which are not in the new configuration.
595 root = logging.root
596 existing = list(root.manager.loggerDict.keys())
597 #The list needs to be sorted so that we can
598 #avoid disabling child loggers of explicitly
599 #named loggers. With a sorted list it is easier
600 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100601 existing.sort()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000602 #We'll keep the list of existing loggers
603 #which are children of named loggers here...
604 child_loggers = []
605 #now set up the new ones...
606 loggers = config.get('loggers', EMPTY_DICT)
607 for name in loggers:
608 if name in existing:
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000609 i = existing.index(name) + 1 # look after name
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000610 prefixed = name + "."
611 pflen = len(prefixed)
612 num_existing = len(existing)
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000613 while i < num_existing:
614 if existing[i][:pflen] == prefixed:
615 child_loggers.append(existing[i])
616 i += 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000617 existing.remove(name)
618 try:
619 self.configure_logger(name, loggers[name])
620 except Exception as e:
621 raise ValueError('Unable to configure logger '
Vinay Sajipaa275822016-10-03 19:45:50 +0100622 '%r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000623
624 #Disable any old loggers. There's no point deleting
625 #them as other threads may continue to hold references
626 #and by disabling them, you stop them doing any logging.
627 #However, don't disable children of named loggers, as that's
628 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000629 #for log in existing:
630 # logger = root.manager.loggerDict[log]
631 # if log in child_loggers:
632 # logger.level = logging.NOTSET
633 # logger.handlers = []
634 # logger.propagate = True
635 # elif disable_existing:
636 # logger.disabled = True
637 _handle_existing_loggers(existing, child_loggers,
638 disable_existing)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000639
640 # And finally, do the root logger
641 root = config.get('root', None)
642 if root:
643 try:
644 self.configure_root(root)
645 except Exception as e:
646 raise ValueError('Unable to configure root '
Vinay Sajipaa275822016-10-03 19:45:50 +0100647 'logger') from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000648 finally:
649 logging._releaseLock()
650
651 def configure_formatter(self, config):
652 """Configure a formatter from a dictionary."""
653 if '()' in config:
654 factory = config['()'] # for use in exception handler
655 try:
656 result = self.configure_custom(config)
657 except TypeError as te:
658 if "'format'" not in str(te):
659 raise
660 #Name of parameter changed from fmt to format.
661 #Retry with old name.
662 #This is so that code can be used with older Python versions
663 #(e.g. by Django)
664 config['fmt'] = config.pop('format')
665 config['()'] = factory
666 result = self.configure_custom(config)
667 else:
668 fmt = config.get('format', None)
669 dfmt = config.get('datefmt', None)
Vinay Sajip28421c62013-03-29 17:56:54 +0000670 style = config.get('style', '%')
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100671 cname = config.get('class', None)
BNMetrics18fb1fb2018-10-15 19:41:36 +0100672
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100673 if not cname:
674 c = logging.Formatter
675 else:
676 c = _resolve(cname)
BNMetrics18fb1fb2018-10-15 19:41:36 +0100677
678 # A TypeError would be raised if "validate" key is passed in with a formatter callable
679 # that does not accept "validate" as a parameter
680 if 'validate' in config: # if user hasn't mentioned it, the default will be fine
681 result = c(fmt, dfmt, style, config['validate'])
682 else:
683 result = c(fmt, dfmt, style)
684
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000685 return result
686
687 def configure_filter(self, config):
688 """Configure a filter from a dictionary."""
689 if '()' in config:
690 result = self.configure_custom(config)
691 else:
692 name = config.get('name', '')
693 result = logging.Filter(name)
694 return result
695
696 def add_filters(self, filterer, filters):
697 """Add filters to a filterer from a list of names."""
698 for f in filters:
699 try:
700 filterer.addFilter(self.config['filters'][f])
701 except Exception as e:
Vinay Sajipaa275822016-10-03 19:45:50 +0100702 raise ValueError('Unable to add filter %r' % f) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000703
704 def configure_handler(self, config):
705 """Configure a handler from a dictionary."""
Vinay Sajip28421c62013-03-29 17:56:54 +0000706 config_copy = dict(config) # for restoring in case of error
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000707 formatter = config.pop('formatter', None)
708 if formatter:
709 try:
710 formatter = self.config['formatters'][formatter]
711 except Exception as e:
712 raise ValueError('Unable to set formatter '
Vinay Sajipaa275822016-10-03 19:45:50 +0100713 '%r' % formatter) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000714 level = config.pop('level', None)
715 filters = config.pop('filters', None)
716 if '()' in config:
717 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200718 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000719 c = self.resolve(c)
720 factory = c
721 else:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000722 cname = config.pop('class')
723 klass = self.resolve(cname)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000724 #Special case for handler which refers to another handler
725 if issubclass(klass, logging.handlers.MemoryHandler) and\
726 'target' in config:
727 try:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000728 th = self.config['handlers'][config['target']]
729 if not isinstance(th, logging.Handler):
Vinay Sajip28421c62013-03-29 17:56:54 +0000730 config.update(config_copy) # restore for deferred cfg
Vinay Sajip3f885b52013-03-22 15:19:54 +0000731 raise TypeError('target not configured yet')
732 config['target'] = th
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000733 except Exception as e:
734 raise ValueError('Unable to set target handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100735 '%r' % config['target']) from e
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000736 elif issubclass(klass, logging.handlers.SMTPHandler) and\
737 'mailhost' in config:
738 config['mailhost'] = self.as_tuple(config['mailhost'])
739 elif issubclass(klass, logging.handlers.SysLogHandler) and\
740 'address' in config:
741 config['address'] = self.as_tuple(config['address'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000742 factory = klass
Vinay Sajip8d270232012-11-15 14:20:18 +0000743 props = config.pop('.', None)
Serhiy Storchaka3f2e6f12018-02-26 16:50:11 +0200744 kwargs = {k: config[k] for k in config if valid_ident(k)}
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000745 try:
746 result = factory(**kwargs)
747 except TypeError as te:
748 if "'stream'" not in str(te):
749 raise
750 #The argument name changed from strm to stream
751 #Retry with old name.
752 #This is so that code can be used with older Python versions
753 #(e.g. by Django)
754 kwargs['strm'] = kwargs.pop('stream')
755 result = factory(**kwargs)
756 if formatter:
757 result.setFormatter(formatter)
758 if level is not None:
759 result.setLevel(logging._checkLevel(level))
760 if filters:
761 self.add_filters(result, filters)
Vinay Sajip8d270232012-11-15 14:20:18 +0000762 if props:
763 for name, value in props.items():
764 setattr(result, name, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000765 return result
766
767 def add_handlers(self, logger, handlers):
768 """Add handlers to a logger from a list of names."""
769 for h in handlers:
770 try:
771 logger.addHandler(self.config['handlers'][h])
772 except Exception as e:
Vinay Sajipaa275822016-10-03 19:45:50 +0100773 raise ValueError('Unable to add handler %r' % h) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000774
775 def common_logger_config(self, logger, config, incremental=False):
776 """
777 Perform configuration which is common to root and non-root loggers.
778 """
779 level = config.get('level', None)
780 if level is not None:
781 logger.setLevel(logging._checkLevel(level))
782 if not incremental:
783 #Remove any existing handlers
784 for h in logger.handlers[:]:
785 logger.removeHandler(h)
786 handlers = config.get('handlers', None)
787 if handlers:
788 self.add_handlers(logger, handlers)
789 filters = config.get('filters', None)
790 if filters:
791 self.add_filters(logger, filters)
792
793 def configure_logger(self, name, config, incremental=False):
794 """Configure a non-root logger from a dictionary."""
795 logger = logging.getLogger(name)
796 self.common_logger_config(logger, config, incremental)
797 propagate = config.get('propagate', None)
798 if propagate is not None:
799 logger.propagate = propagate
800
801 def configure_root(self, config, incremental=False):
802 """Configure a root logger from a dictionary."""
803 root = logging.getLogger()
804 self.common_logger_config(root, config, incremental)
805
806dictConfigClass = DictConfigurator
807
808def dictConfig(config):
809 """Configure logging using a dictionary."""
810 dictConfigClass(config).configure()
811
812
Vinay Sajip4ded5512012-10-02 15:56:16 +0100813def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000814 """
815 Start up a socket server on the specified port, and listen for new
816 configurations.
817
818 These will be sent as a file suitable for processing by fileConfig().
819 Returns a Thread object on which you can call start() to start the server,
820 and which you can join() when appropriate. To stop the server, call
821 stopListening().
Vinay Sajip3e763da2012-10-02 16:15:33 +0100822
823 Use the ``verify`` argument to verify any bytes received across the wire
824 from a client. If specified, it should be a callable which receives a
825 single argument - the bytes of configuration data received across the
826 network - and it should return either ``None``, to indicate that the
827 passed in bytes could not be verified and should be discarded, or a
828 byte string which is then passed to the configuration machinery as
829 normal. Note that you can return transformed bytes, e.g. by decrypting
830 the bytes passed in.
Guido van Rossum57102f82002-11-13 16:15:58 +0000831 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000832
833 class ConfigStreamHandler(StreamRequestHandler):
834 """
835 Handler for a logging configuration request.
836
837 It expects a completely new logging configuration and uses fileConfig
838 to install it.
839 """
840 def handle(self):
841 """
842 Handle a request.
843
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000844 Each request is expected to be a 4-byte length, packed using
845 struct.pack(">L", n), followed by the config file.
846 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000847 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000848 try:
849 conn = self.connection
850 chunk = conn.recv(4)
851 if len(chunk) == 4:
852 slen = struct.unpack(">L", chunk)[0]
853 chunk = self.connection.recv(slen)
854 while len(chunk) < slen:
855 chunk = chunk + conn.recv(slen - len(chunk))
Vinay Sajip4ded5512012-10-02 15:56:16 +0100856 if self.server.verify is not None:
857 chunk = self.server.verify(chunk)
858 if chunk is not None: # verified, can process
859 chunk = chunk.decode("utf-8")
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000860 try:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100861 import json
862 d =json.loads(chunk)
863 assert isinstance(d, dict)
864 dictConfig(d)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100865 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100866 #Apply new configuration.
867
868 file = io.StringIO(chunk)
869 try:
870 fileConfig(file)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100871 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100872 traceback.print_exc()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000873 if self.server.ready:
874 self.server.ready.set()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200875 except OSError as e:
Vinay Sajip71dcb282014-03-20 13:03:17 +0000876 if e.errno != RESET_ERROR:
Guido van Rossum57102f82002-11-13 16:15:58 +0000877 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000878
879 class ConfigSocketReceiver(ThreadingTCPServer):
880 """
881 A simple TCP socket-based logging config receiver.
882 """
883
884 allow_reuse_address = 1
885
886 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100887 handler=None, ready=None, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000888 ThreadingTCPServer.__init__(self, (host, port), handler)
889 logging._acquireLock()
890 self.abort = 0
891 logging._releaseLock()
892 self.timeout = 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000893 self.ready = ready
Vinay Sajip4ded5512012-10-02 15:56:16 +0100894 self.verify = verify
Guido van Rossum57102f82002-11-13 16:15:58 +0000895
896 def serve_until_stopped(self):
897 import select
898 abort = 0
899 while not abort:
900 rd, wr, ex = select.select([self.socket.fileno()],
901 [], [],
902 self.timeout)
903 if rd:
904 self.handle_request()
905 logging._acquireLock()
906 abort = self.abort
907 logging._releaseLock()
Victor Stinner97d7e652017-09-13 01:44:08 -0700908 self.server_close()
Guido van Rossum57102f82002-11-13 16:15:58 +0000909
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000910 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000911
Vinay Sajip4ded5512012-10-02 15:56:16 +0100912 def __init__(self, rcvr, hdlr, port, verify):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000913 super(Server, self).__init__()
914 self.rcvr = rcvr
915 self.hdlr = hdlr
916 self.port = port
Vinay Sajip4ded5512012-10-02 15:56:16 +0100917 self.verify = verify
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000918 self.ready = threading.Event()
919
920 def run(self):
921 server = self.rcvr(port=self.port, handler=self.hdlr,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100922 ready=self.ready,
923 verify=self.verify)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000924 if self.port == 0:
925 self.port = server.server_address[1]
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000926 self.ready.set()
927 global _listener
928 logging._acquireLock()
929 _listener = server
930 logging._releaseLock()
931 server.serve_until_stopped()
932
Vinay Sajip4ded5512012-10-02 15:56:16 +0100933 return Server(ConfigSocketReceiver, ConfigStreamHandler, port, verify)
Guido van Rossum57102f82002-11-13 16:15:58 +0000934
935def stopListening():
936 """
937 Stop the listening server which was created with a call to listen().
938 """
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000939 global _listener
Vinay Sajip9fdd11b2010-09-25 17:48:25 +0000940 logging._acquireLock()
941 try:
942 if _listener:
943 _listener.abort = 1
944 _listener = None
945 finally:
Guido van Rossum57102f82002-11-13 16:15:58 +0000946 logging._releaseLock()