blob: 7927e7618777e5bb3731d6843326f54cdb8b2c35 [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:
Vinay Sajip989b69a2006-01-16 21:28:37 +000076 logging._handlers.clear()
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000077 del logging._handlerList[:]
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
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000268IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
269
270
271def valid_ident(s):
272 m = IDENTIFIER.match(s)
273 if not m:
274 raise ValueError('Not a valid Python identifier: %r' % s)
275 return True
276
277
Vinay Sajipb1698d42014-03-20 13:14:39 +0000278class ConvertingMixin(object):
279 """For ConvertingXXX's, this mixin class provides common functions"""
280
281 def convert_with_key(self, key, value, replace=True):
282 result = self.configurator.convert(value)
283 #If the converted value is different, save for next time
284 if value is not result:
285 if replace:
286 self[key] = result
287 if type(result) in (ConvertingDict, ConvertingList,
288 ConvertingTuple):
289 result.parent = self
290 result.key = key
291 return result
292
293 def convert(self, value):
294 result = self.configurator.convert(value)
295 if value is not result:
296 if type(result) in (ConvertingDict, ConvertingList,
297 ConvertingTuple):
298 result.parent = self
299 return result
300
301
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000302# The ConvertingXXX classes are wrappers around standard Python containers,
303# and they serve to convert any suitable values in the container. The
304# conversion converts base dicts, lists and tuples to their wrapped
305# equivalents, whereas strings which match a conversion format are converted
306# appropriately.
307#
308# Each wrapper should have a configurator attribute holding the actual
309# configurator to use for conversion.
310
Vinay Sajipb1698d42014-03-20 13:14:39 +0000311class ConvertingDict(dict, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000312 """A converting dictionary wrapper."""
313
314 def __getitem__(self, key):
315 value = dict.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000316 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000317
318 def get(self, key, default=None):
319 value = dict.get(self, key, default)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000320 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000321
322 def pop(self, key, default=None):
323 value = dict.pop(self, key, default)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000324 return self.convert_with_key(key, value, replace=False)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000325
Vinay Sajipb1698d42014-03-20 13:14:39 +0000326class ConvertingList(list, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000327 """A converting list wrapper."""
328 def __getitem__(self, key):
329 value = list.__getitem__(self, key)
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, idx=-1):
333 value = list.pop(self, idx)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000334 return self.convert(value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000335
Vinay Sajipb1698d42014-03-20 13:14:39 +0000336class ConvertingTuple(tuple, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000337 """A converting tuple wrapper."""
338 def __getitem__(self, key):
339 value = tuple.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000340 # Can't replace a tuple entry.
341 return self.convert_with_key(key, value, replace=False)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000342
343class BaseConfigurator(object):
344 """
345 The configurator base class which defines some useful defaults.
346 """
347
348 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
349
350 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
351 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
352 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
353 DIGIT_PATTERN = re.compile(r'^\d+$')
354
355 value_converters = {
356 'ext' : 'ext_convert',
357 'cfg' : 'cfg_convert',
358 }
359
360 # We might want to use a different one, e.g. importlib
Brett Cannonc2368502010-06-12 00:39:28 +0000361 importer = staticmethod(__import__)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000362
363 def __init__(self, config):
364 self.config = ConvertingDict(config)
365 self.config.configurator = self
366
367 def resolve(self, s):
368 """
369 Resolve strings to objects using standard import and attribute
370 syntax.
371 """
372 name = s.split('.')
373 used = name.pop(0)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000374 try:
375 found = self.importer(used)
376 for frag in name:
377 used += '.' + frag
378 try:
379 found = getattr(found, frag)
380 except AttributeError:
381 self.importer(used)
382 found = getattr(found, frag)
383 return found
384 except ImportError:
385 e, tb = sys.exc_info()[1:]
386 v = ValueError('Cannot resolve %r: %s' % (s, e))
387 v.__cause__, v.__traceback__ = e, tb
388 raise v
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000389
390 def ext_convert(self, value):
391 """Default converter for the ext:// protocol."""
392 return self.resolve(value)
393
394 def cfg_convert(self, value):
395 """Default converter for the cfg:// protocol."""
396 rest = value
397 m = self.WORD_PATTERN.match(rest)
398 if m is None:
399 raise ValueError("Unable to convert %r" % value)
400 else:
401 rest = rest[m.end():]
402 d = self.config[m.groups()[0]]
403 #print d, rest
404 while rest:
405 m = self.DOT_PATTERN.match(rest)
406 if m:
407 d = d[m.groups()[0]]
408 else:
409 m = self.INDEX_PATTERN.match(rest)
410 if m:
411 idx = m.groups()[0]
412 if not self.DIGIT_PATTERN.match(idx):
413 d = d[idx]
414 else:
415 try:
416 n = int(idx) # try as number first (most likely)
417 d = d[n]
418 except TypeError:
419 d = d[idx]
420 if m:
421 rest = rest[m.end():]
422 else:
423 raise ValueError('Unable to convert '
424 '%r at %r' % (value, rest))
425 #rest should be empty
426 return d
427
428 def convert(self, value):
429 """
430 Convert values to an appropriate type. dicts, lists and tuples are
431 replaced by their converting alternatives. Strings are checked to
432 see if they have a conversion format and are converted if they do.
433 """
434 if not isinstance(value, ConvertingDict) and isinstance(value, dict):
435 value = ConvertingDict(value)
436 value.configurator = self
437 elif not isinstance(value, ConvertingList) and isinstance(value, list):
438 value = ConvertingList(value)
439 value.configurator = self
440 elif not isinstance(value, ConvertingTuple) and\
441 isinstance(value, tuple):
442 value = ConvertingTuple(value)
443 value.configurator = self
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000444 elif isinstance(value, str): # str for py3k
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000445 m = self.CONVERT_PATTERN.match(value)
446 if m:
447 d = m.groupdict()
448 prefix = d['prefix']
449 converter = self.value_converters.get(prefix, None)
450 if converter:
451 suffix = d['suffix']
452 converter = getattr(self, converter)
453 value = converter(suffix)
454 return value
455
456 def configure_custom(self, config):
457 """Configure an object with a user-supplied factory."""
458 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200459 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000460 c = self.resolve(c)
461 props = config.pop('.', None)
462 # Check for valid identifiers
Jon Dufresne39726282017-05-18 07:35:54 -0700463 kwargs = dict((k, config[k]) for k in config if valid_ident(k))
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000464 result = c(**kwargs)
465 if props:
466 for name, value in props.items():
467 setattr(result, name, value)
468 return result
469
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000470 def as_tuple(self, value):
471 """Utility function which converts lists to tuples."""
472 if isinstance(value, list):
473 value = tuple(value)
474 return value
475
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000476class DictConfigurator(BaseConfigurator):
477 """
478 Configure logging using a dictionary-like object to describe the
479 configuration.
480 """
481
482 def configure(self):
483 """Do the configuration."""
484
485 config = self.config
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000486 if 'version' not in config:
487 raise ValueError("dictionary doesn't specify a version")
488 if config['version'] != 1:
489 raise ValueError("Unsupported version: %s" % config['version'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000490 incremental = config.pop('incremental', False)
491 EMPTY_DICT = {}
492 logging._acquireLock()
493 try:
494 if incremental:
495 handlers = config.get('handlers', EMPTY_DICT)
496 for name in handlers:
497 if name not in logging._handlers:
498 raise ValueError('No handler found with '
499 'name %r' % name)
500 else:
501 try:
502 handler = logging._handlers[name]
503 handler_config = handlers[name]
504 level = handler_config.get('level', None)
505 if level:
506 handler.setLevel(logging._checkLevel(level))
507 except Exception as e:
508 raise ValueError('Unable to configure handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100509 '%r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000510 loggers = config.get('loggers', EMPTY_DICT)
511 for name in loggers:
512 try:
513 self.configure_logger(name, loggers[name], True)
514 except Exception as e:
515 raise ValueError('Unable to configure logger '
Vinay Sajipaa275822016-10-03 19:45:50 +0100516 '%r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000517 root = config.get('root', None)
518 if root:
519 try:
520 self.configure_root(root, True)
521 except Exception as e:
522 raise ValueError('Unable to configure root '
Vinay Sajipaa275822016-10-03 19:45:50 +0100523 'logger') from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000524 else:
525 disable_existing = config.pop('disable_existing_loggers', True)
526
527 logging._handlers.clear()
528 del logging._handlerList[:]
529
530 # Do formatters first - they don't refer to anything else
531 formatters = config.get('formatters', EMPTY_DICT)
532 for name in formatters:
533 try:
534 formatters[name] = self.configure_formatter(
535 formatters[name])
536 except Exception as e:
537 raise ValueError('Unable to configure '
Vinay Sajipaa275822016-10-03 19:45:50 +0100538 'formatter %r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000539 # Next, do filters - they don't refer to anything else, either
540 filters = config.get('filters', EMPTY_DICT)
541 for name in filters:
542 try:
543 filters[name] = self.configure_filter(filters[name])
544 except Exception as e:
545 raise ValueError('Unable to configure '
Vinay Sajipaa275822016-10-03 19:45:50 +0100546 'filter %r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000547
548 # Next, do handlers - they refer to formatters and filters
549 # As handlers can refer to other handlers, sort the keys
550 # to allow a deterministic order of configuration
551 handlers = config.get('handlers', EMPTY_DICT)
Vinay Sajip3f885b52013-03-22 15:19:54 +0000552 deferred = []
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000553 for name in sorted(handlers):
554 try:
555 handler = self.configure_handler(handlers[name])
556 handler.name = name
557 handlers[name] = handler
558 except Exception as e:
Vinay Sajipb7403432016-10-03 19:50:56 +0100559 if 'target not configured yet' in str(e.__cause__):
Vinay Sajip3f885b52013-03-22 15:19:54 +0000560 deferred.append(name)
561 else:
562 raise ValueError('Unable to configure handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100563 '%r' % name) from e
Vinay Sajip3f885b52013-03-22 15:19:54 +0000564
565 # Now do any that were deferred
566 for name in deferred:
567 try:
568 handler = self.configure_handler(handlers[name])
569 handler.name = name
570 handlers[name] = handler
571 except Exception as e:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000572 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
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000575 # Next, do loggers - they refer to handlers and filters
576
577 #we don't want to lose the existing loggers,
578 #since other threads may have pointers to them.
579 #existing is set to contain all existing loggers,
580 #and as we go through the new configuration we
581 #remove any which are configured. At the end,
582 #what's left in existing is the set of loggers
583 #which were in the previous configuration but
584 #which are not in the new configuration.
585 root = logging.root
586 existing = list(root.manager.loggerDict.keys())
587 #The list needs to be sorted so that we can
588 #avoid disabling child loggers of explicitly
589 #named loggers. With a sorted list it is easier
590 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100591 existing.sort()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000592 #We'll keep the list of existing loggers
593 #which are children of named loggers here...
594 child_loggers = []
595 #now set up the new ones...
596 loggers = config.get('loggers', EMPTY_DICT)
597 for name in loggers:
598 if name in existing:
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000599 i = existing.index(name) + 1 # look after name
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000600 prefixed = name + "."
601 pflen = len(prefixed)
602 num_existing = len(existing)
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000603 while i < num_existing:
604 if existing[i][:pflen] == prefixed:
605 child_loggers.append(existing[i])
606 i += 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000607 existing.remove(name)
608 try:
609 self.configure_logger(name, loggers[name])
610 except Exception as e:
611 raise ValueError('Unable to configure logger '
Vinay Sajipaa275822016-10-03 19:45:50 +0100612 '%r' % name) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000613
614 #Disable any old loggers. There's no point deleting
615 #them as other threads may continue to hold references
616 #and by disabling them, you stop them doing any logging.
617 #However, don't disable children of named loggers, as that's
618 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000619 #for log in existing:
620 # logger = root.manager.loggerDict[log]
621 # if log in child_loggers:
622 # logger.level = logging.NOTSET
623 # logger.handlers = []
624 # logger.propagate = True
625 # elif disable_existing:
626 # logger.disabled = True
627 _handle_existing_loggers(existing, child_loggers,
628 disable_existing)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000629
630 # And finally, do the root logger
631 root = config.get('root', None)
632 if root:
633 try:
634 self.configure_root(root)
635 except Exception as e:
636 raise ValueError('Unable to configure root '
Vinay Sajipaa275822016-10-03 19:45:50 +0100637 'logger') from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000638 finally:
639 logging._releaseLock()
640
641 def configure_formatter(self, config):
642 """Configure a formatter from a dictionary."""
643 if '()' in config:
644 factory = config['()'] # for use in exception handler
645 try:
646 result = self.configure_custom(config)
647 except TypeError as te:
648 if "'format'" not in str(te):
649 raise
650 #Name of parameter changed from fmt to format.
651 #Retry with old name.
652 #This is so that code can be used with older Python versions
653 #(e.g. by Django)
654 config['fmt'] = config.pop('format')
655 config['()'] = factory
656 result = self.configure_custom(config)
657 else:
658 fmt = config.get('format', None)
659 dfmt = config.get('datefmt', None)
Vinay Sajip28421c62013-03-29 17:56:54 +0000660 style = config.get('style', '%')
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100661 cname = config.get('class', None)
662 if not cname:
663 c = logging.Formatter
664 else:
665 c = _resolve(cname)
666 result = c(fmt, dfmt, style)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000667 return result
668
669 def configure_filter(self, config):
670 """Configure a filter from a dictionary."""
671 if '()' in config:
672 result = self.configure_custom(config)
673 else:
674 name = config.get('name', '')
675 result = logging.Filter(name)
676 return result
677
678 def add_filters(self, filterer, filters):
679 """Add filters to a filterer from a list of names."""
680 for f in filters:
681 try:
682 filterer.addFilter(self.config['filters'][f])
683 except Exception as e:
Vinay Sajipaa275822016-10-03 19:45:50 +0100684 raise ValueError('Unable to add filter %r' % f) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000685
686 def configure_handler(self, config):
687 """Configure a handler from a dictionary."""
Vinay Sajip28421c62013-03-29 17:56:54 +0000688 config_copy = dict(config) # for restoring in case of error
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000689 formatter = config.pop('formatter', None)
690 if formatter:
691 try:
692 formatter = self.config['formatters'][formatter]
693 except Exception as e:
694 raise ValueError('Unable to set formatter '
Vinay Sajipaa275822016-10-03 19:45:50 +0100695 '%r' % formatter) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000696 level = config.pop('level', None)
697 filters = config.pop('filters', None)
698 if '()' in config:
699 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200700 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000701 c = self.resolve(c)
702 factory = c
703 else:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000704 cname = config.pop('class')
705 klass = self.resolve(cname)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000706 #Special case for handler which refers to another handler
707 if issubclass(klass, logging.handlers.MemoryHandler) and\
708 'target' in config:
709 try:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000710 th = self.config['handlers'][config['target']]
711 if not isinstance(th, logging.Handler):
Vinay Sajip28421c62013-03-29 17:56:54 +0000712 config.update(config_copy) # restore for deferred cfg
Vinay Sajip3f885b52013-03-22 15:19:54 +0000713 raise TypeError('target not configured yet')
714 config['target'] = th
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000715 except Exception as e:
716 raise ValueError('Unable to set target handler '
Vinay Sajipaa275822016-10-03 19:45:50 +0100717 '%r' % config['target']) from e
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000718 elif issubclass(klass, logging.handlers.SMTPHandler) and\
719 'mailhost' in config:
720 config['mailhost'] = self.as_tuple(config['mailhost'])
721 elif issubclass(klass, logging.handlers.SysLogHandler) and\
722 'address' in config:
723 config['address'] = self.as_tuple(config['address'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000724 factory = klass
Vinay Sajip8d270232012-11-15 14:20:18 +0000725 props = config.pop('.', None)
Jon Dufresne39726282017-05-18 07:35:54 -0700726 kwargs = dict((k, config[k]) for k in config if valid_ident(k))
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000727 try:
728 result = factory(**kwargs)
729 except TypeError as te:
730 if "'stream'" not in str(te):
731 raise
732 #The argument name changed from strm to stream
733 #Retry with old name.
734 #This is so that code can be used with older Python versions
735 #(e.g. by Django)
736 kwargs['strm'] = kwargs.pop('stream')
737 result = factory(**kwargs)
738 if formatter:
739 result.setFormatter(formatter)
740 if level is not None:
741 result.setLevel(logging._checkLevel(level))
742 if filters:
743 self.add_filters(result, filters)
Vinay Sajip8d270232012-11-15 14:20:18 +0000744 if props:
745 for name, value in props.items():
746 setattr(result, name, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000747 return result
748
749 def add_handlers(self, logger, handlers):
750 """Add handlers to a logger from a list of names."""
751 for h in handlers:
752 try:
753 logger.addHandler(self.config['handlers'][h])
754 except Exception as e:
Vinay Sajipaa275822016-10-03 19:45:50 +0100755 raise ValueError('Unable to add handler %r' % h) from e
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000756
757 def common_logger_config(self, logger, config, incremental=False):
758 """
759 Perform configuration which is common to root and non-root loggers.
760 """
761 level = config.get('level', None)
762 if level is not None:
763 logger.setLevel(logging._checkLevel(level))
764 if not incremental:
765 #Remove any existing handlers
766 for h in logger.handlers[:]:
767 logger.removeHandler(h)
768 handlers = config.get('handlers', None)
769 if handlers:
770 self.add_handlers(logger, handlers)
771 filters = config.get('filters', None)
772 if filters:
773 self.add_filters(logger, filters)
774
775 def configure_logger(self, name, config, incremental=False):
776 """Configure a non-root logger from a dictionary."""
777 logger = logging.getLogger(name)
778 self.common_logger_config(logger, config, incremental)
779 propagate = config.get('propagate', None)
780 if propagate is not None:
781 logger.propagate = propagate
782
783 def configure_root(self, config, incremental=False):
784 """Configure a root logger from a dictionary."""
785 root = logging.getLogger()
786 self.common_logger_config(root, config, incremental)
787
788dictConfigClass = DictConfigurator
789
790def dictConfig(config):
791 """Configure logging using a dictionary."""
792 dictConfigClass(config).configure()
793
794
Vinay Sajip4ded5512012-10-02 15:56:16 +0100795def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000796 """
797 Start up a socket server on the specified port, and listen for new
798 configurations.
799
800 These will be sent as a file suitable for processing by fileConfig().
801 Returns a Thread object on which you can call start() to start the server,
802 and which you can join() when appropriate. To stop the server, call
803 stopListening().
Vinay Sajip3e763da2012-10-02 16:15:33 +0100804
805 Use the ``verify`` argument to verify any bytes received across the wire
806 from a client. If specified, it should be a callable which receives a
807 single argument - the bytes of configuration data received across the
808 network - and it should return either ``None``, to indicate that the
809 passed in bytes could not be verified and should be discarded, or a
810 byte string which is then passed to the configuration machinery as
811 normal. Note that you can return transformed bytes, e.g. by decrypting
812 the bytes passed in.
Guido van Rossum57102f82002-11-13 16:15:58 +0000813 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000814
815 class ConfigStreamHandler(StreamRequestHandler):
816 """
817 Handler for a logging configuration request.
818
819 It expects a completely new logging configuration and uses fileConfig
820 to install it.
821 """
822 def handle(self):
823 """
824 Handle a request.
825
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000826 Each request is expected to be a 4-byte length, packed using
827 struct.pack(">L", n), followed by the config file.
828 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000829 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000830 try:
831 conn = self.connection
832 chunk = conn.recv(4)
833 if len(chunk) == 4:
834 slen = struct.unpack(">L", chunk)[0]
835 chunk = self.connection.recv(slen)
836 while len(chunk) < slen:
837 chunk = chunk + conn.recv(slen - len(chunk))
Vinay Sajip4ded5512012-10-02 15:56:16 +0100838 if self.server.verify is not None:
839 chunk = self.server.verify(chunk)
840 if chunk is not None: # verified, can process
841 chunk = chunk.decode("utf-8")
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000842 try:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100843 import json
844 d =json.loads(chunk)
845 assert isinstance(d, dict)
846 dictConfig(d)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100847 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100848 #Apply new configuration.
849
850 file = io.StringIO(chunk)
851 try:
852 fileConfig(file)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100853 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100854 traceback.print_exc()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000855 if self.server.ready:
856 self.server.ready.set()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200857 except OSError as e:
Vinay Sajip71dcb282014-03-20 13:03:17 +0000858 if e.errno != RESET_ERROR:
Guido van Rossum57102f82002-11-13 16:15:58 +0000859 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000860
861 class ConfigSocketReceiver(ThreadingTCPServer):
862 """
863 A simple TCP socket-based logging config receiver.
864 """
865
866 allow_reuse_address = 1
867
868 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100869 handler=None, ready=None, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000870 ThreadingTCPServer.__init__(self, (host, port), handler)
871 logging._acquireLock()
872 self.abort = 0
873 logging._releaseLock()
874 self.timeout = 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000875 self.ready = ready
Vinay Sajip4ded5512012-10-02 15:56:16 +0100876 self.verify = verify
Guido van Rossum57102f82002-11-13 16:15:58 +0000877
878 def serve_until_stopped(self):
879 import select
880 abort = 0
881 while not abort:
882 rd, wr, ex = select.select([self.socket.fileno()],
883 [], [],
884 self.timeout)
885 if rd:
886 self.handle_request()
887 logging._acquireLock()
888 abort = self.abort
889 logging._releaseLock()
Victor Stinner97d7e652017-09-13 01:44:08 -0700890 self.server_close()
Guido van Rossum57102f82002-11-13 16:15:58 +0000891
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000892 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000893
Vinay Sajip4ded5512012-10-02 15:56:16 +0100894 def __init__(self, rcvr, hdlr, port, verify):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000895 super(Server, self).__init__()
896 self.rcvr = rcvr
897 self.hdlr = hdlr
898 self.port = port
Vinay Sajip4ded5512012-10-02 15:56:16 +0100899 self.verify = verify
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000900 self.ready = threading.Event()
901
902 def run(self):
903 server = self.rcvr(port=self.port, handler=self.hdlr,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100904 ready=self.ready,
905 verify=self.verify)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000906 if self.port == 0:
907 self.port = server.server_address[1]
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000908 self.ready.set()
909 global _listener
910 logging._acquireLock()
911 _listener = server
912 logging._releaseLock()
913 server.serve_until_stopped()
914
Vinay Sajip4ded5512012-10-02 15:56:16 +0100915 return Server(ConfigSocketReceiver, ConfigStreamHandler, port, verify)
Guido van Rossum57102f82002-11-13 16:15:58 +0000916
917def stopListening():
918 """
919 Stop the listening server which was created with a call to listen().
920 """
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000921 global _listener
Vinay Sajip9fdd11b2010-09-25 17:48:25 +0000922 logging._acquireLock()
923 try:
924 if _listener:
925 _listener.abort = 1
926 _listener = None
927 finally:
Guido van Rossum57102f82002-11-13 16:15:58 +0000928 logging._releaseLock()