blob: 4a3b8966ede1c098720a3d5e4effc6e867b30d04 [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
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)
Lucas Cimonb15100f2019-10-31 09:06:25 +0100146 h.name = hand
Łukasz Langa26d513c2010-11-10 18:57:39 +0000147 if "level" in section:
148 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700149 h.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000150 if len(fmt):
151 h.setFormatter(formatters[fmt])
Benjamin Peterson41181742008-07-02 20:22:54 +0000152 if issubclass(klass, logging.handlers.MemoryHandler):
Łukasz Langa26d513c2010-11-10 18:57:39 +0000153 target = section.get("target", "")
Vinay Sajip989b69a2006-01-16 21:28:37 +0000154 if len(target): #the target handler may not be loaded yet, so keep for later...
155 fixups.append((h, target))
156 handlers[hand] = h
157 #now all handlers are loaded, fixup inter-handler references...
158 for h, t in fixups:
159 h.setTarget(handlers[t])
160 return handlers
161
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000162def _handle_existing_loggers(existing, child_loggers, disable_existing):
163 """
164 When (re)configuring logging, handle loggers which were in the previous
165 configuration but are not in the new configuration. There's no point
166 deleting them as other threads may continue to hold references to them;
167 and by disabling them, you stop them doing any logging.
Vinay Sajip989b69a2006-01-16 21:28:37 +0000168
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000169 However, don't disable children of named loggers, as that's probably not
170 what was intended by the user. Also, allow existing loggers to NOT be
171 disabled if disable_existing is false.
172 """
173 root = logging.root
174 for log in existing:
175 logger = root.manager.loggerDict[log]
176 if log in child_loggers:
Vinay Sajip1d094af2019-09-22 03:51:51 +0100177 if not isinstance(logger, logging.PlaceHolder):
178 logger.setLevel(logging.NOTSET)
179 logger.handlers = []
180 logger.propagate = True
Vinay Sajip68b4cc82013-03-23 11:18:45 +0000181 else:
182 logger.disabled = disable_existing
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000183
184def _install_loggers(cp, handlers, disable_existing):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000185 """Create and install loggers"""
186
187 # configure the root first
Łukasz Langa26d513c2010-11-10 18:57:39 +0000188 llist = cp["loggers"]["keys"]
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000189 llist = llist.split(",")
sanjayp2ae4ad72017-11-15 14:58:11 +0530190 llist = list(_strip_spaces(llist))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000191 llist.remove("root")
Łukasz Langa26d513c2010-11-10 18:57:39 +0000192 section = cp["logger_root"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000193 root = logging.root
194 log = root
Łukasz Langa26d513c2010-11-10 18:57:39 +0000195 if "level" in section:
196 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700197 log.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000198 for h in root.handlers[:]:
199 root.removeHandler(h)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000200 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000201 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000202 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000203 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000204 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000205 log.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000206
207 #and now the others...
208 #we don't want to lose the existing loggers,
209 #since other threads may have pointers to them.
210 #existing is set to contain all existing loggers,
211 #and as we go through the new configuration we
212 #remove any which are configured. At the end,
213 #what's left in existing is the set of loggers
214 #which were in the previous configuration but
215 #which are not in the new configuration.
Guido van Rossum8b8a5432007-02-12 00:07:01 +0000216 existing = list(root.manager.loggerDict.keys())
Christian Heimes96f31632007-11-12 01:32:03 +0000217 #The list needs to be sorted so that we can
218 #avoid disabling child loggers of explicitly
219 #named loggers. With a sorted list it is easier
220 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100221 existing.sort()
Christian Heimes96f31632007-11-12 01:32:03 +0000222 #We'll keep the list of existing loggers
223 #which are children of named loggers here...
224 child_loggers = []
Vinay Sajip989b69a2006-01-16 21:28:37 +0000225 #now set up the new ones...
226 for log in llist:
Łukasz Langa26d513c2010-11-10 18:57:39 +0000227 section = cp["logger_%s" % log]
228 qn = section["qualname"]
229 propagate = section.getint("propagate", fallback=1)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000230 logger = logging.getLogger(qn)
231 if qn in existing:
Vinay Sajip3f84b072011-03-07 17:49:33 +0000232 i = existing.index(qn) + 1 # start with the entry after qn
Christian Heimes96f31632007-11-12 01:32:03 +0000233 prefixed = qn + "."
234 pflen = len(prefixed)
235 num_existing = len(existing)
Vinay Sajip3f84b072011-03-07 17:49:33 +0000236 while i < num_existing:
237 if existing[i][:pflen] == prefixed:
238 child_loggers.append(existing[i])
239 i += 1
Vinay Sajip989b69a2006-01-16 21:28:37 +0000240 existing.remove(qn)
Łukasz Langa26d513c2010-11-10 18:57:39 +0000241 if "level" in section:
242 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700243 logger.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000244 for h in logger.handlers[:]:
245 logger.removeHandler(h)
246 logger.propagate = propagate
247 logger.disabled = 0
Łukasz Langa26d513c2010-11-10 18:57:39 +0000248 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000249 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000250 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000251 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000252 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000253 logger.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000254
255 #Disable any old loggers. There's no point deleting
256 #them as other threads may continue to hold references
257 #and by disabling them, you stop them doing any logging.
Christian Heimes96f31632007-11-12 01:32:03 +0000258 #However, don't disable children of named loggers, as that's
259 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000260 #for log in existing:
261 # logger = root.manager.loggerDict[log]
262 # if log in child_loggers:
263 # logger.level = logging.NOTSET
264 # logger.handlers = []
265 # logger.propagate = 1
266 # elif disable_existing_loggers:
267 # logger.disabled = 1
268 _handle_existing_loggers(existing, child_loggers, disable_existing)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000269
Xtreak087570a2018-07-02 14:27:46 +0530270
271def _clearExistingHandlers():
272 """Clear and close existing handlers"""
273 logging._handlers.clear()
274 logging.shutdown(logging._handlerList[:])
275 del logging._handlerList[:]
276
277
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000278IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
279
280
281def valid_ident(s):
282 m = IDENTIFIER.match(s)
283 if not m:
284 raise ValueError('Not a valid Python identifier: %r' % s)
285 return True
286
287
Vinay Sajipb1698d42014-03-20 13:14:39 +0000288class ConvertingMixin(object):
289 """For ConvertingXXX's, this mixin class provides common functions"""
290
291 def convert_with_key(self, key, value, replace=True):
292 result = self.configurator.convert(value)
293 #If the converted value is different, save for next time
294 if value is not result:
295 if replace:
296 self[key] = result
297 if type(result) in (ConvertingDict, ConvertingList,
298 ConvertingTuple):
299 result.parent = self
300 result.key = key
301 return result
302
303 def convert(self, value):
304 result = self.configurator.convert(value)
305 if value is not result:
306 if type(result) in (ConvertingDict, ConvertingList,
307 ConvertingTuple):
308 result.parent = self
309 return result
310
311
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000312# The ConvertingXXX classes are wrappers around standard Python containers,
313# and they serve to convert any suitable values in the container. The
314# conversion converts base dicts, lists and tuples to their wrapped
315# equivalents, whereas strings which match a conversion format are converted
316# appropriately.
317#
318# Each wrapper should have a configurator attribute holding the actual
319# configurator to use for conversion.
320
Vinay Sajipb1698d42014-03-20 13:14:39 +0000321class ConvertingDict(dict, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000322 """A converting dictionary wrapper."""
323
324 def __getitem__(self, key):
325 value = dict.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000326 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000327
328 def get(self, key, default=None):
329 value = dict.get(self, key, default)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000330 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000331
332 def pop(self, key, default=None):
333 value = dict.pop(self, key, default)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000334 return self.convert_with_key(key, value, replace=False)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000335
Vinay Sajipb1698d42014-03-20 13:14:39 +0000336class ConvertingList(list, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000337 """A converting list wrapper."""
338 def __getitem__(self, key):
339 value = list.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000340 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000341
342 def pop(self, idx=-1):
343 value = list.pop(self, idx)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000344 return self.convert(value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000345
Vinay Sajipb1698d42014-03-20 13:14:39 +0000346class ConvertingTuple(tuple, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000347 """A converting tuple wrapper."""
348 def __getitem__(self, key):
349 value = tuple.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000350 # Can't replace a tuple entry.
351 return self.convert_with_key(key, value, replace=False)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000352
353class BaseConfigurator(object):
354 """
355 The configurator base class which defines some useful defaults.
356 """
357
358 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
359
360 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
361 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
362 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
363 DIGIT_PATTERN = re.compile(r'^\d+$')
364
365 value_converters = {
366 'ext' : 'ext_convert',
367 'cfg' : 'cfg_convert',
368 }
369
370 # We might want to use a different one, e.g. importlib
Brett Cannonc2368502010-06-12 00:39:28 +0000371 importer = staticmethod(__import__)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000372
373 def __init__(self, config):
374 self.config = ConvertingDict(config)
375 self.config.configurator = self
376
377 def resolve(self, s):
378 """
379 Resolve strings to objects using standard import and attribute
380 syntax.
381 """
382 name = s.split('.')
383 used = name.pop(0)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000384 try:
385 found = self.importer(used)
386 for frag in name:
387 used += '.' + frag
388 try:
389 found = getattr(found, frag)
390 except AttributeError:
391 self.importer(used)
392 found = getattr(found, frag)
393 return found
394 except ImportError:
395 e, tb = sys.exc_info()[1:]
396 v = ValueError('Cannot resolve %r: %s' % (s, e))
397 v.__cause__, v.__traceback__ = e, tb
398 raise v
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000399
400 def ext_convert(self, value):
401 """Default converter for the ext:// protocol."""
402 return self.resolve(value)
403
404 def cfg_convert(self, value):
405 """Default converter for the cfg:// protocol."""
406 rest = value
407 m = self.WORD_PATTERN.match(rest)
408 if m is None:
409 raise ValueError("Unable to convert %r" % value)
410 else:
411 rest = rest[m.end():]
412 d = self.config[m.groups()[0]]
413 #print d, rest
414 while rest:
415 m = self.DOT_PATTERN.match(rest)
416 if m:
417 d = d[m.groups()[0]]
418 else:
419 m = self.INDEX_PATTERN.match(rest)
420 if m:
421 idx = m.groups()[0]
422 if not self.DIGIT_PATTERN.match(idx):
423 d = d[idx]
424 else:
425 try:
426 n = int(idx) # try as number first (most likely)
427 d = d[n]
428 except TypeError:
429 d = d[idx]
430 if m:
431 rest = rest[m.end():]
432 else:
433 raise ValueError('Unable to convert '
434 '%r at %r' % (value, rest))
435 #rest should be empty
436 return d
437
438 def convert(self, value):
439 """
440 Convert values to an appropriate type. dicts, lists and tuples are
441 replaced by their converting alternatives. Strings are checked to
442 see if they have a conversion format and are converted if they do.
443 """
444 if not isinstance(value, ConvertingDict) and isinstance(value, dict):
445 value = ConvertingDict(value)
446 value.configurator = self
447 elif not isinstance(value, ConvertingList) and isinstance(value, list):
448 value = ConvertingList(value)
449 value.configurator = self
450 elif not isinstance(value, ConvertingTuple) and\
451 isinstance(value, tuple):
452 value = ConvertingTuple(value)
453 value.configurator = self
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000454 elif isinstance(value, str): # str for py3k
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000455 m = self.CONVERT_PATTERN.match(value)
456 if m:
457 d = m.groupdict()
458 prefix = d['prefix']
459 converter = self.value_converters.get(prefix, None)
460 if converter:
461 suffix = d['suffix']
462 converter = getattr(self, converter)
463 value = converter(suffix)
464 return value
465
466 def configure_custom(self, config):
467 """Configure an object with a user-supplied factory."""
468 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200469 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000470 c = self.resolve(c)
471 props = config.pop('.', None)
472 # Check for valid identifiers
Serhiy Storchaka3f2e6f12018-02-26 16:50:11 +0200473 kwargs = {k: config[k] for k in config if valid_ident(k)}
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000474 result = c(**kwargs)
475 if props:
476 for name, value in props.items():
477 setattr(result, name, value)
478 return result
479
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000480 def as_tuple(self, value):
481 """Utility function which converts lists to tuples."""
482 if isinstance(value, list):
483 value = tuple(value)
484 return value
485
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000486class DictConfigurator(BaseConfigurator):
487 """
488 Configure logging using a dictionary-like object to describe the
489 configuration.
490 """
491
492 def configure(self):
493 """Do the configuration."""
494
495 config = self.config
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000496 if 'version' not in config:
497 raise ValueError("dictionary doesn't specify a version")
498 if config['version'] != 1:
499 raise ValueError("Unsupported version: %s" % config['version'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000500 incremental = config.pop('incremental', False)
501 EMPTY_DICT = {}
502 logging._acquireLock()
503 try:
504 if incremental:
505 handlers = config.get('handlers', EMPTY_DICT)
506 for name in handlers:
507 if name not in logging._handlers:
508 raise ValueError('No handler found with '
509 'name %r' % name)
510 else:
511 try:
512 handler = logging._handlers[name]
513 handler_config = handlers[name]
514 level = handler_config.get('level', None)
515 if level:
516 handler.setLevel(logging._checkLevel(level))
517 except Exception as e:
518 raise ValueError('Unable to configure handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100519 '%r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000520 loggers = config.get('loggers', EMPTY_DICT)
521 for name in loggers:
522 try:
523 self.configure_logger(name, loggers[name], True)
524 except Exception as e:
525 raise ValueError('Unable to configure logger '
Vinay Sajipaa275822016-10-03 19:45:50 +0100526 '%r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000527 root = config.get('root', None)
528 if root:
529 try:
530 self.configure_root(root, True)
531 except Exception as e:
532 raise ValueError('Unable to configure root '
Vinay Sajipaa275822016-10-03 19:45:50 +0100533 'logger') from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000534 else:
535 disable_existing = config.pop('disable_existing_loggers', True)
536
Xtreak087570a2018-07-02 14:27:46 +0530537 _clearExistingHandlers()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000538
539 # Do formatters first - they don't refer to anything else
540 formatters = config.get('formatters', EMPTY_DICT)
541 for name in formatters:
542 try:
543 formatters[name] = self.configure_formatter(
544 formatters[name])
545 except Exception as e:
546 raise ValueError('Unable to configure '
Vinay Sajipaa275822016-10-03 19:45:50 +0100547 'formatter %r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000548 # Next, do filters - they don't refer to anything else, either
549 filters = config.get('filters', EMPTY_DICT)
550 for name in filters:
551 try:
552 filters[name] = self.configure_filter(filters[name])
553 except Exception as e:
554 raise ValueError('Unable to configure '
Vinay Sajipaa275822016-10-03 19:45:50 +0100555 'filter %r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000556
557 # Next, do handlers - they refer to formatters and filters
558 # As handlers can refer to other handlers, sort the keys
559 # to allow a deterministic order of configuration
560 handlers = config.get('handlers', EMPTY_DICT)
Vinay Sajip3f885b52013-03-22 15:19:54 +0000561 deferred = []
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000562 for name in sorted(handlers):
563 try:
564 handler = self.configure_handler(handlers[name])
565 handler.name = name
566 handlers[name] = handler
567 except Exception as e:
Vinay Sajipb7403432016-10-03 19:50:56 +0100568 if 'target not configured yet' in str(e.__cause__):
Vinay Sajip3f885b52013-03-22 15:19:54 +0000569 deferred.append(name)
570 else:
571 raise ValueError('Unable to configure handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100572 '%r' % name) from e
Vinay Sajip3f885b52013-03-22 15:19:54 +0000573
574 # Now do any that were deferred
575 for name in deferred:
576 try:
577 handler = self.configure_handler(handlers[name])
578 handler.name = name
579 handlers[name] = handler
580 except Exception as e:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000581 raise ValueError('Unable to configure handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100582 '%r' % name) from e
Vinay Sajip3f885b52013-03-22 15:19:54 +0000583
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000584 # Next, do loggers - they refer to handlers and filters
585
586 #we don't want to lose the existing loggers,
587 #since other threads may have pointers to them.
588 #existing is set to contain all existing loggers,
589 #and as we go through the new configuration we
590 #remove any which are configured. At the end,
591 #what's left in existing is the set of loggers
592 #which were in the previous configuration but
593 #which are not in the new configuration.
594 root = logging.root
595 existing = list(root.manager.loggerDict.keys())
596 #The list needs to be sorted so that we can
597 #avoid disabling child loggers of explicitly
598 #named loggers. With a sorted list it is easier
599 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100600 existing.sort()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000601 #We'll keep the list of existing loggers
602 #which are children of named loggers here...
603 child_loggers = []
604 #now set up the new ones...
605 loggers = config.get('loggers', EMPTY_DICT)
606 for name in loggers:
607 if name in existing:
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000608 i = existing.index(name) + 1 # look after name
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000609 prefixed = name + "."
610 pflen = len(prefixed)
611 num_existing = len(existing)
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000612 while i < num_existing:
613 if existing[i][:pflen] == prefixed:
614 child_loggers.append(existing[i])
615 i += 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000616 existing.remove(name)
617 try:
618 self.configure_logger(name, loggers[name])
619 except Exception as e:
620 raise ValueError('Unable to configure logger '
Vinay Sajipaa275822016-10-03 19:45:50 +0100621 '%r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000622
623 #Disable any old loggers. There's no point deleting
624 #them as other threads may continue to hold references
625 #and by disabling them, you stop them doing any logging.
626 #However, don't disable children of named loggers, as that's
627 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000628 #for log in existing:
629 # logger = root.manager.loggerDict[log]
630 # if log in child_loggers:
631 # logger.level = logging.NOTSET
632 # logger.handlers = []
633 # logger.propagate = True
634 # elif disable_existing:
635 # logger.disabled = True
636 _handle_existing_loggers(existing, child_loggers,
637 disable_existing)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000638
639 # And finally, do the root logger
640 root = config.get('root', None)
641 if root:
642 try:
643 self.configure_root(root)
644 except Exception as e:
645 raise ValueError('Unable to configure root '
Vinay Sajipaa275822016-10-03 19:45:50 +0100646 'logger') from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000647 finally:
648 logging._releaseLock()
649
650 def configure_formatter(self, config):
651 """Configure a formatter from a dictionary."""
652 if '()' in config:
653 factory = config['()'] # for use in exception handler
654 try:
655 result = self.configure_custom(config)
656 except TypeError as te:
657 if "'format'" not in str(te):
658 raise
659 #Name of parameter changed from fmt to format.
660 #Retry with old name.
661 #This is so that code can be used with older Python versions
662 #(e.g. by Django)
663 config['fmt'] = config.pop('format')
664 config['()'] = factory
665 result = self.configure_custom(config)
666 else:
667 fmt = config.get('format', None)
668 dfmt = config.get('datefmt', None)
Vinay Sajip28421c62013-03-29 17:56:54 +0000669 style = config.get('style', '%')
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100670 cname = config.get('class', None)
BNMetrics18fb1fb2018-10-15 19:41:36 +0100671
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100672 if not cname:
673 c = logging.Formatter
674 else:
675 c = _resolve(cname)
BNMetrics18fb1fb2018-10-15 19:41:36 +0100676
677 # A TypeError would be raised if "validate" key is passed in with a formatter callable
678 # that does not accept "validate" as a parameter
679 if 'validate' in config: # if user hasn't mentioned it, the default will be fine
680 result = c(fmt, dfmt, style, config['validate'])
681 else:
682 result = c(fmt, dfmt, style)
683
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000684 return result
685
686 def configure_filter(self, config):
687 """Configure a filter from a dictionary."""
688 if '()' in config:
689 result = self.configure_custom(config)
690 else:
691 name = config.get('name', '')
692 result = logging.Filter(name)
693 return result
694
695 def add_filters(self, filterer, filters):
696 """Add filters to a filterer from a list of names."""
697 for f in filters:
698 try:
699 filterer.addFilter(self.config['filters'][f])
700 except Exception as e:
Vinay Sajipaa275822016-10-03 19:45:50 +0100701 raise ValueError('Unable to add filter %r' % f) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000702
703 def configure_handler(self, config):
704 """Configure a handler from a dictionary."""
Vinay Sajip28421c62013-03-29 17:56:54 +0000705 config_copy = dict(config) # for restoring in case of error
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000706 formatter = config.pop('formatter', None)
707 if formatter:
708 try:
709 formatter = self.config['formatters'][formatter]
710 except Exception as e:
711 raise ValueError('Unable to set formatter '
Vinay Sajipaa275822016-10-03 19:45:50 +0100712 '%r' % formatter) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000713 level = config.pop('level', None)
714 filters = config.pop('filters', None)
715 if '()' in config:
716 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200717 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000718 c = self.resolve(c)
719 factory = c
720 else:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000721 cname = config.pop('class')
722 klass = self.resolve(cname)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000723 #Special case for handler which refers to another handler
724 if issubclass(klass, logging.handlers.MemoryHandler) and\
725 'target' in config:
726 try:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000727 th = self.config['handlers'][config['target']]
728 if not isinstance(th, logging.Handler):
Vinay Sajip28421c62013-03-29 17:56:54 +0000729 config.update(config_copy) # restore for deferred cfg
Vinay Sajip3f885b52013-03-22 15:19:54 +0000730 raise TypeError('target not configured yet')
731 config['target'] = th
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000732 except Exception as e:
733 raise ValueError('Unable to set target handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100734 '%r' % config['target']) from e
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000735 elif issubclass(klass, logging.handlers.SMTPHandler) and\
736 'mailhost' in config:
737 config['mailhost'] = self.as_tuple(config['mailhost'])
738 elif issubclass(klass, logging.handlers.SysLogHandler) and\
739 'address' in config:
740 config['address'] = self.as_tuple(config['address'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000741 factory = klass
Vinay Sajip8d270232012-11-15 14:20:18 +0000742 props = config.pop('.', None)
Serhiy Storchaka3f2e6f12018-02-26 16:50:11 +0200743 kwargs = {k: config[k] for k in config if valid_ident(k)}
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000744 try:
745 result = factory(**kwargs)
746 except TypeError as te:
747 if "'stream'" not in str(te):
748 raise
749 #The argument name changed from strm to stream
750 #Retry with old name.
751 #This is so that code can be used with older Python versions
752 #(e.g. by Django)
753 kwargs['strm'] = kwargs.pop('stream')
754 result = factory(**kwargs)
755 if formatter:
756 result.setFormatter(formatter)
757 if level is not None:
758 result.setLevel(logging._checkLevel(level))
759 if filters:
760 self.add_filters(result, filters)
Vinay Sajip8d270232012-11-15 14:20:18 +0000761 if props:
762 for name, value in props.items():
763 setattr(result, name, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000764 return result
765
766 def add_handlers(self, logger, handlers):
767 """Add handlers to a logger from a list of names."""
768 for h in handlers:
769 try:
770 logger.addHandler(self.config['handlers'][h])
771 except Exception as e:
Vinay Sajipaa275822016-10-03 19:45:50 +0100772 raise ValueError('Unable to add handler %r' % h) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000773
774 def common_logger_config(self, logger, config, incremental=False):
775 """
776 Perform configuration which is common to root and non-root loggers.
777 """
778 level = config.get('level', None)
779 if level is not None:
780 logger.setLevel(logging._checkLevel(level))
781 if not incremental:
782 #Remove any existing handlers
783 for h in logger.handlers[:]:
784 logger.removeHandler(h)
785 handlers = config.get('handlers', None)
786 if handlers:
787 self.add_handlers(logger, handlers)
788 filters = config.get('filters', None)
789 if filters:
790 self.add_filters(logger, filters)
791
792 def configure_logger(self, name, config, incremental=False):
793 """Configure a non-root logger from a dictionary."""
794 logger = logging.getLogger(name)
795 self.common_logger_config(logger, config, incremental)
796 propagate = config.get('propagate', None)
797 if propagate is not None:
798 logger.propagate = propagate
799
800 def configure_root(self, config, incremental=False):
801 """Configure a root logger from a dictionary."""
802 root = logging.getLogger()
803 self.common_logger_config(root, config, incremental)
804
805dictConfigClass = DictConfigurator
806
807def dictConfig(config):
808 """Configure logging using a dictionary."""
809 dictConfigClass(config).configure()
810
811
Vinay Sajip4ded5512012-10-02 15:56:16 +0100812def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000813 """
814 Start up a socket server on the specified port, and listen for new
815 configurations.
816
817 These will be sent as a file suitable for processing by fileConfig().
818 Returns a Thread object on which you can call start() to start the server,
819 and which you can join() when appropriate. To stop the server, call
820 stopListening().
Vinay Sajip3e763da2012-10-02 16:15:33 +0100821
822 Use the ``verify`` argument to verify any bytes received across the wire
823 from a client. If specified, it should be a callable which receives a
824 single argument - the bytes of configuration data received across the
825 network - and it should return either ``None``, to indicate that the
826 passed in bytes could not be verified and should be discarded, or a
827 byte string which is then passed to the configuration machinery as
828 normal. Note that you can return transformed bytes, e.g. by decrypting
829 the bytes passed in.
Guido van Rossum57102f82002-11-13 16:15:58 +0000830 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000831
832 class ConfigStreamHandler(StreamRequestHandler):
833 """
834 Handler for a logging configuration request.
835
836 It expects a completely new logging configuration and uses fileConfig
837 to install it.
838 """
839 def handle(self):
840 """
841 Handle a request.
842
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000843 Each request is expected to be a 4-byte length, packed using
844 struct.pack(">L", n), followed by the config file.
845 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000846 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000847 try:
848 conn = self.connection
849 chunk = conn.recv(4)
850 if len(chunk) == 4:
851 slen = struct.unpack(">L", chunk)[0]
852 chunk = self.connection.recv(slen)
853 while len(chunk) < slen:
854 chunk = chunk + conn.recv(slen - len(chunk))
Vinay Sajip4ded5512012-10-02 15:56:16 +0100855 if self.server.verify is not None:
856 chunk = self.server.verify(chunk)
857 if chunk is not None: # verified, can process
858 chunk = chunk.decode("utf-8")
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000859 try:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100860 import json
861 d =json.loads(chunk)
862 assert isinstance(d, dict)
863 dictConfig(d)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100864 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100865 #Apply new configuration.
866
867 file = io.StringIO(chunk)
868 try:
869 fileConfig(file)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100870 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100871 traceback.print_exc()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000872 if self.server.ready:
873 self.server.ready.set()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200874 except OSError as e:
Vinay Sajip71dcb282014-03-20 13:03:17 +0000875 if e.errno != RESET_ERROR:
Guido van Rossum57102f82002-11-13 16:15:58 +0000876 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000877
878 class ConfigSocketReceiver(ThreadingTCPServer):
879 """
880 A simple TCP socket-based logging config receiver.
881 """
882
883 allow_reuse_address = 1
884
885 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100886 handler=None, ready=None, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000887 ThreadingTCPServer.__init__(self, (host, port), handler)
888 logging._acquireLock()
889 self.abort = 0
890 logging._releaseLock()
891 self.timeout = 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000892 self.ready = ready
Vinay Sajip4ded5512012-10-02 15:56:16 +0100893 self.verify = verify
Guido van Rossum57102f82002-11-13 16:15:58 +0000894
895 def serve_until_stopped(self):
896 import select
897 abort = 0
898 while not abort:
899 rd, wr, ex = select.select([self.socket.fileno()],
900 [], [],
901 self.timeout)
902 if rd:
903 self.handle_request()
904 logging._acquireLock()
905 abort = self.abort
906 logging._releaseLock()
Victor Stinner97d7e652017-09-13 01:44:08 -0700907 self.server_close()
Guido van Rossum57102f82002-11-13 16:15:58 +0000908
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000909 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000910
Vinay Sajip4ded5512012-10-02 15:56:16 +0100911 def __init__(self, rcvr, hdlr, port, verify):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000912 super(Server, self).__init__()
913 self.rcvr = rcvr
914 self.hdlr = hdlr
915 self.port = port
Vinay Sajip4ded5512012-10-02 15:56:16 +0100916 self.verify = verify
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000917 self.ready = threading.Event()
918
919 def run(self):
920 server = self.rcvr(port=self.port, handler=self.hdlr,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100921 ready=self.ready,
922 verify=self.verify)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000923 if self.port == 0:
924 self.port = server.server_address[1]
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000925 self.ready.set()
926 global _listener
927 logging._acquireLock()
928 _listener = server
929 logging._releaseLock()
930 server.serve_until_stopped()
931
Vinay Sajip4ded5512012-10-02 15:56:16 +0100932 return Server(ConfigSocketReceiver, ConfigStreamHandler, port, verify)
Guido van Rossum57102f82002-11-13 16:15:58 +0000933
934def stopListening():
935 """
936 Stop the listening server which was created with a call to listen().
937 """
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000938 global _listener
Vinay Sajip9fdd11b2010-09-25 17:48:25 +0000939 logging._acquireLock()
940 try:
941 if _listener:
942 _listener.abort = 1
943 _listener = None
944 finally:
Guido van Rossum57102f82002-11-13 16:15:58 +0000945 logging._releaseLock()