blob: dbfd2c62eed24dfb610dbf2fb609a05f6662c139 [file] [log] [blame]
Vinay Sajip3f885b52013-03-22 15:19:54 +00001# Copyright 2001-2013 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 Sajip3f885b52013-03-22 15:19:54 +000022Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved.
Guido van Rossum57102f82002-11-13 16:15:58 +000023
24To use, simply 'import logging' and log away!
25"""
26
Florent Xicluna5252f9f2011-11-07 19:43:05 +010027import sys, logging, logging.handlers, socket, struct, traceback, re
28import io
Vinay Sajip612df8e2005-02-18 11:54:46 +000029
30try:
Georg Brandl2067bfd2008-05-25 13:05:15 +000031 import _thread as thread
Vinay Sajip612df8e2005-02-18 11:54:46 +000032 import threading
Vinay Sajip985ef872011-04-26 19:34:04 +010033except ImportError: #pragma: no cover
Vinay Sajip612df8e2005-02-18 11:54:46 +000034 thread = None
Guido van Rossum57102f82002-11-13 16:15:58 +000035
Alexandre Vassalottice261952008-05-12 02:31:37 +000036from socketserver import ThreadingTCPServer, StreamRequestHandler
Guido van Rossum57102f82002-11-13 16:15:58 +000037
38
39DEFAULT_LOGGING_CONFIG_PORT = 9030
40
Vinay Sajip326441e2004-02-20 13:16:36 +000041if sys.platform == "win32":
42 RESET_ERROR = 10054 #WSAECONNRESET
43else:
44 RESET_ERROR = 104 #ECONNRESET
45
Guido van Rossum57102f82002-11-13 16:15:58 +000046#
47# The following code implements a socket listener for on-the-fly
48# reconfiguration of logging.
49#
50# _listener holds the server object doing the listening
51_listener = None
52
Georg Brandl472f2e22009-06-08 08:58:54 +000053def fileConfig(fname, defaults=None, disable_existing_loggers=True):
Guido van Rossum57102f82002-11-13 16:15:58 +000054 """
55 Read the logging configuration from a ConfigParser-format file.
56
57 This can be called several times from an application, allowing an end user
58 the ability to select from various pre-canned configurations (if the
59 developer provides a mechanism to present the choices and load the chosen
60 configuration).
Guido van Rossum57102f82002-11-13 16:15:58 +000061 """
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +000062 import configparser
Guido van Rossum57102f82002-11-13 16:15:58 +000063
Vinay Sajipcf9e2f22012-10-09 09:06:03 +010064 if isinstance(fname, configparser.RawConfigParser):
65 cp = fname
Guido van Rossum57102f82002-11-13 16:15:58 +000066 else:
Vinay Sajipcf9e2f22012-10-09 09:06:03 +010067 cp = configparser.ConfigParser(defaults)
68 if hasattr(fname, 'readline'):
69 cp.read_file(fname)
70 else:
71 cp.read(fname)
Vinay Sajip989b69a2006-01-16 21:28:37 +000072
73 formatters = _create_formatters(cp)
74
75 # critical section
Guido van Rossum57102f82002-11-13 16:15:58 +000076 logging._acquireLock()
77 try:
Vinay Sajip989b69a2006-01-16 21:28:37 +000078 logging._handlers.clear()
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000079 del logging._handlerList[:]
Vinay Sajip989b69a2006-01-16 21:28:37 +000080 # Handlers add themselves to logging._handlers
81 handlers = _install_handlers(cp, formatters)
Benjamin Petersonfea6a942008-07-02 16:11:42 +000082 _install_loggers(cp, handlers, disable_existing_loggers)
Guido van Rossum57102f82002-11-13 16:15:58 +000083 finally:
84 logging._releaseLock()
85
Vinay Sajip989b69a2006-01-16 21:28:37 +000086
Vinay Sajip7a7160b2006-01-20 18:28:03 +000087def _resolve(name):
88 """Resolve a dotted name to a global object."""
Neal Norwitz9d72bb42007-04-17 08:48:32 +000089 name = name.split('.')
Vinay Sajip7a7160b2006-01-20 18:28:03 +000090 used = name.pop(0)
91 found = __import__(used)
92 for n in name:
93 used = used + '.' + n
94 try:
95 found = getattr(found, n)
96 except AttributeError:
97 __import__(used)
98 found = getattr(found, n)
99 return found
100
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000101def _strip_spaces(alist):
102 return map(lambda x: x.strip(), alist)
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000103
Vinay Sajip989b69a2006-01-16 21:28:37 +0000104def _create_formatters(cp):
105 """Create and return formatters"""
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000106 flist = cp["formatters"]["keys"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000107 if not len(flist):
108 return {}
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000109 flist = flist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000110 flist = _strip_spaces(flist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000111 formatters = {}
112 for form in flist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000113 sectname = "formatter_%s" % form
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000114 fs = cp.get(sectname, "format", raw=True, fallback=None)
115 dfs = cp.get(sectname, "datefmt", raw=True, fallback=None)
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 Sajip7a7160b2006-01-20 18:28:03 +0000120 f = c(fs, dfs)
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)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000142 args = section["args"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000143 args = eval(args, vars(logging))
Neal Norwitzd9108552006-03-17 08:00:19 +0000144 h = klass(*args)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000145 if "level" in section:
146 level = section["level"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000147 h.setLevel(logging._levelNames[level])
148 if len(fmt):
149 h.setFormatter(formatters[fmt])
Benjamin Peterson41181742008-07-02 20:22:54 +0000150 if issubclass(klass, logging.handlers.MemoryHandler):
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000151 target = section.get("target", "")
Vinay Sajip989b69a2006-01-16 21:28:37 +0000152 if len(target): #the target handler may not be loaded yet, so keep for later...
153 fixups.append((h, target))
154 handlers[hand] = h
155 #now all handlers are loaded, fixup inter-handler references...
156 for h, t in fixups:
157 h.setTarget(handlers[t])
158 return handlers
159
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000160def _handle_existing_loggers(existing, child_loggers, disable_existing):
161 """
162 When (re)configuring logging, handle loggers which were in the previous
163 configuration but are not in the new configuration. There's no point
164 deleting them as other threads may continue to hold references to them;
165 and by disabling them, you stop them doing any logging.
Vinay Sajip989b69a2006-01-16 21:28:37 +0000166
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000167 However, don't disable children of named loggers, as that's probably not
168 what was intended by the user. Also, allow existing loggers to NOT be
169 disabled if disable_existing is false.
170 """
171 root = logging.root
172 for log in existing:
173 logger = root.manager.loggerDict[log]
174 if log in child_loggers:
175 logger.level = logging.NOTSET
176 logger.handlers = []
177 logger.propagate = True
Vinay Sajip68b4cc82013-03-23 11:18:45 +0000178 else:
179 logger.disabled = disable_existing
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000180
181def _install_loggers(cp, handlers, disable_existing):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000182 """Create and install loggers"""
183
184 # configure the root first
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000185 llist = cp["loggers"]["keys"]
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000186 llist = llist.split(",")
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000187 llist = list(map(lambda x: x.strip(), llist))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000188 llist.remove("root")
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000189 section = cp["logger_root"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000190 root = logging.root
191 log = root
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000192 if "level" in section:
193 level = section["level"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000194 log.setLevel(logging._levelNames[level])
195 for h in root.handlers[:]:
196 root.removeHandler(h)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000197 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000198 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000199 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000200 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000201 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000202 log.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000203
204 #and now the others...
205 #we don't want to lose the existing loggers,
206 #since other threads may have pointers to them.
207 #existing is set to contain all existing loggers,
208 #and as we go through the new configuration we
209 #remove any which are configured. At the end,
210 #what's left in existing is the set of loggers
211 #which were in the previous configuration but
212 #which are not in the new configuration.
Guido van Rossum8b8a5432007-02-12 00:07:01 +0000213 existing = list(root.manager.loggerDict.keys())
Christian Heimes96f31632007-11-12 01:32:03 +0000214 #The list needs to be sorted so that we can
215 #avoid disabling child loggers of explicitly
216 #named loggers. With a sorted list it is easier
217 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100218 existing.sort()
Christian Heimes96f31632007-11-12 01:32:03 +0000219 #We'll keep the list of existing loggers
220 #which are children of named loggers here...
221 child_loggers = []
Vinay Sajip989b69a2006-01-16 21:28:37 +0000222 #now set up the new ones...
223 for log in llist:
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000224 section = cp["logger_%s" % log]
225 qn = section["qualname"]
226 propagate = section.getint("propagate", fallback=1)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000227 logger = logging.getLogger(qn)
228 if qn in existing:
Vinay Sajip3f84b072011-03-07 17:49:33 +0000229 i = existing.index(qn) + 1 # start with the entry after qn
Christian Heimes96f31632007-11-12 01:32:03 +0000230 prefixed = qn + "."
231 pflen = len(prefixed)
232 num_existing = len(existing)
Vinay Sajip3f84b072011-03-07 17:49:33 +0000233 while i < num_existing:
234 if existing[i][:pflen] == prefixed:
235 child_loggers.append(existing[i])
236 i += 1
Vinay Sajip989b69a2006-01-16 21:28:37 +0000237 existing.remove(qn)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000238 if "level" in section:
239 level = section["level"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000240 logger.setLevel(logging._levelNames[level])
241 for h in logger.handlers[:]:
242 logger.removeHandler(h)
243 logger.propagate = propagate
244 logger.disabled = 0
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000245 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000246 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000247 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000248 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000249 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000250 logger.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000251
252 #Disable any old loggers. There's no point deleting
253 #them as other threads may continue to hold references
254 #and by disabling them, you stop them doing any logging.
Christian Heimes96f31632007-11-12 01:32:03 +0000255 #However, don't disable children of named loggers, as that's
256 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000257 #for log in existing:
258 # logger = root.manager.loggerDict[log]
259 # if log in child_loggers:
260 # logger.level = logging.NOTSET
261 # logger.handlers = []
262 # logger.propagate = 1
263 # elif disable_existing_loggers:
264 # logger.disabled = 1
265 _handle_existing_loggers(existing, child_loggers, disable_existing)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000266
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000267IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
268
269
270def valid_ident(s):
271 m = IDENTIFIER.match(s)
272 if not m:
273 raise ValueError('Not a valid Python identifier: %r' % s)
274 return True
275
276
277# The ConvertingXXX classes are wrappers around standard Python containers,
278# and they serve to convert any suitable values in the container. The
279# conversion converts base dicts, lists and tuples to their wrapped
280# equivalents, whereas strings which match a conversion format are converted
281# appropriately.
282#
283# Each wrapper should have a configurator attribute holding the actual
284# configurator to use for conversion.
285
286class ConvertingDict(dict):
287 """A converting dictionary wrapper."""
288
289 def __getitem__(self, key):
290 value = dict.__getitem__(self, key)
291 result = self.configurator.convert(value)
292 #If the converted value is different, save for next time
293 if value is not result:
294 self[key] = result
295 if type(result) in (ConvertingDict, ConvertingList,
296 ConvertingTuple):
297 result.parent = self
298 result.key = key
299 return result
300
301 def get(self, key, default=None):
302 value = dict.get(self, key, default)
303 result = self.configurator.convert(value)
304 #If the converted value is different, save for next time
305 if value is not result:
306 self[key] = result
307 if type(result) in (ConvertingDict, ConvertingList,
308 ConvertingTuple):
309 result.parent = self
310 result.key = key
311 return result
312
313 def pop(self, key, default=None):
314 value = dict.pop(self, key, default)
315 result = self.configurator.convert(value)
316 if value is not result:
317 if type(result) in (ConvertingDict, ConvertingList,
318 ConvertingTuple):
319 result.parent = self
320 result.key = key
321 return result
322
323class ConvertingList(list):
324 """A converting list wrapper."""
325 def __getitem__(self, key):
326 value = list.__getitem__(self, key)
327 result = self.configurator.convert(value)
328 #If the converted value is different, save for next time
329 if value is not result:
330 self[key] = result
331 if type(result) in (ConvertingDict, ConvertingList,
332 ConvertingTuple):
333 result.parent = self
334 result.key = key
335 return result
336
337 def pop(self, idx=-1):
338 value = list.pop(self, idx)
339 result = self.configurator.convert(value)
340 if value is not result:
341 if type(result) in (ConvertingDict, ConvertingList,
342 ConvertingTuple):
343 result.parent = self
344 return result
345
346class ConvertingTuple(tuple):
347 """A converting tuple wrapper."""
348 def __getitem__(self, key):
349 value = tuple.__getitem__(self, key)
350 result = self.configurator.convert(value)
351 if value is not result:
352 if type(result) in (ConvertingDict, ConvertingList,
353 ConvertingTuple):
354 result.parent = self
355 result.key = key
356 return result
357
358class BaseConfigurator(object):
359 """
360 The configurator base class which defines some useful defaults.
361 """
362
363 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
364
365 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
366 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
367 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
368 DIGIT_PATTERN = re.compile(r'^\d+$')
369
370 value_converters = {
371 'ext' : 'ext_convert',
372 'cfg' : 'cfg_convert',
373 }
374
375 # We might want to use a different one, e.g. importlib
Brett Cannonc2368502010-06-12 00:39:28 +0000376 importer = staticmethod(__import__)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000377
378 def __init__(self, config):
379 self.config = ConvertingDict(config)
380 self.config.configurator = self
381
382 def resolve(self, s):
383 """
384 Resolve strings to objects using standard import and attribute
385 syntax.
386 """
387 name = s.split('.')
388 used = name.pop(0)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000389 try:
390 found = self.importer(used)
391 for frag in name:
392 used += '.' + frag
393 try:
394 found = getattr(found, frag)
395 except AttributeError:
396 self.importer(used)
397 found = getattr(found, frag)
398 return found
399 except ImportError:
400 e, tb = sys.exc_info()[1:]
401 v = ValueError('Cannot resolve %r: %s' % (s, e))
402 v.__cause__, v.__traceback__ = e, tb
403 raise v
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000404
405 def ext_convert(self, value):
406 """Default converter for the ext:// protocol."""
407 return self.resolve(value)
408
409 def cfg_convert(self, value):
410 """Default converter for the cfg:// protocol."""
411 rest = value
412 m = self.WORD_PATTERN.match(rest)
413 if m is None:
414 raise ValueError("Unable to convert %r" % value)
415 else:
416 rest = rest[m.end():]
417 d = self.config[m.groups()[0]]
418 #print d, rest
419 while rest:
420 m = self.DOT_PATTERN.match(rest)
421 if m:
422 d = d[m.groups()[0]]
423 else:
424 m = self.INDEX_PATTERN.match(rest)
425 if m:
426 idx = m.groups()[0]
427 if not self.DIGIT_PATTERN.match(idx):
428 d = d[idx]
429 else:
430 try:
431 n = int(idx) # try as number first (most likely)
432 d = d[n]
433 except TypeError:
434 d = d[idx]
435 if m:
436 rest = rest[m.end():]
437 else:
438 raise ValueError('Unable to convert '
439 '%r at %r' % (value, rest))
440 #rest should be empty
441 return d
442
443 def convert(self, value):
444 """
445 Convert values to an appropriate type. dicts, lists and tuples are
446 replaced by their converting alternatives. Strings are checked to
447 see if they have a conversion format and are converted if they do.
448 """
449 if not isinstance(value, ConvertingDict) and isinstance(value, dict):
450 value = ConvertingDict(value)
451 value.configurator = self
452 elif not isinstance(value, ConvertingList) and isinstance(value, list):
453 value = ConvertingList(value)
454 value.configurator = self
455 elif not isinstance(value, ConvertingTuple) and\
456 isinstance(value, tuple):
457 value = ConvertingTuple(value)
458 value.configurator = self
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000459 elif isinstance(value, str): # str for py3k
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000460 m = self.CONVERT_PATTERN.match(value)
461 if m:
462 d = m.groupdict()
463 prefix = d['prefix']
464 converter = self.value_converters.get(prefix, None)
465 if converter:
466 suffix = d['suffix']
467 converter = getattr(self, converter)
468 value = converter(suffix)
469 return value
470
471 def configure_custom(self, config):
472 """Configure an object with a user-supplied factory."""
473 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200474 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000475 c = self.resolve(c)
476 props = config.pop('.', None)
477 # Check for valid identifiers
478 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
479 result = c(**kwargs)
480 if props:
481 for name, value in props.items():
482 setattr(result, name, value)
483 return result
484
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000485 def as_tuple(self, value):
486 """Utility function which converts lists to tuples."""
487 if isinstance(value, list):
488 value = tuple(value)
489 return value
490
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000491class DictConfigurator(BaseConfigurator):
492 """
493 Configure logging using a dictionary-like object to describe the
494 configuration.
495 """
496
497 def configure(self):
498 """Do the configuration."""
499
500 config = self.config
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000501 if 'version' not in config:
502 raise ValueError("dictionary doesn't specify a version")
503 if config['version'] != 1:
504 raise ValueError("Unsupported version: %s" % config['version'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000505 incremental = config.pop('incremental', False)
506 EMPTY_DICT = {}
507 logging._acquireLock()
508 try:
509 if incremental:
510 handlers = config.get('handlers', EMPTY_DICT)
511 for name in handlers:
512 if name not in logging._handlers:
513 raise ValueError('No handler found with '
514 'name %r' % name)
515 else:
516 try:
517 handler = logging._handlers[name]
518 handler_config = handlers[name]
519 level = handler_config.get('level', None)
520 if level:
521 handler.setLevel(logging._checkLevel(level))
522 except Exception as e:
523 raise ValueError('Unable to configure handler '
524 '%r: %s' % (name, e))
525 loggers = config.get('loggers', EMPTY_DICT)
526 for name in loggers:
527 try:
528 self.configure_logger(name, loggers[name], True)
529 except Exception as e:
530 raise ValueError('Unable to configure logger '
531 '%r: %s' % (name, e))
532 root = config.get('root', None)
533 if root:
534 try:
535 self.configure_root(root, True)
536 except Exception as e:
537 raise ValueError('Unable to configure root '
538 'logger: %s' % e)
539 else:
540 disable_existing = config.pop('disable_existing_loggers', True)
541
542 logging._handlers.clear()
543 del logging._handlerList[:]
544
545 # Do formatters first - they don't refer to anything else
546 formatters = config.get('formatters', EMPTY_DICT)
547 for name in formatters:
548 try:
549 formatters[name] = self.configure_formatter(
550 formatters[name])
551 except Exception as e:
552 raise ValueError('Unable to configure '
553 'formatter %r: %s' % (name, e))
554 # Next, do filters - they don't refer to anything else, either
555 filters = config.get('filters', EMPTY_DICT)
556 for name in filters:
557 try:
558 filters[name] = self.configure_filter(filters[name])
559 except Exception as e:
560 raise ValueError('Unable to configure '
561 'filter %r: %s' % (name, e))
562
563 # Next, do handlers - they refer to formatters and filters
564 # As handlers can refer to other handlers, sort the keys
565 # to allow a deterministic order of configuration
566 handlers = config.get('handlers', EMPTY_DICT)
Vinay Sajip3f885b52013-03-22 15:19:54 +0000567 deferred = []
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000568 for name in sorted(handlers):
569 try:
570 handler = self.configure_handler(handlers[name])
571 handler.name = name
572 handlers[name] = handler
573 except Exception as e:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000574 if 'target not configured yet' in str(e):
575 deferred.append(name)
576 else:
577 raise ValueError('Unable to configure handler '
578 '%r: %s' % (name, e))
579
580 # Now do any that were deferred
581 for name in deferred:
582 try:
583 handler = self.configure_handler(handlers[name])
584 handler.name = name
585 handlers[name] = handler
586 except Exception as e:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000587 raise ValueError('Unable to configure handler '
588 '%r: %s' % (name, e))
Vinay Sajip3f885b52013-03-22 15:19:54 +0000589
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000590 # Next, do loggers - they refer to handlers and filters
591
592 #we don't want to lose the existing loggers,
593 #since other threads may have pointers to them.
594 #existing is set to contain all existing loggers,
595 #and as we go through the new configuration we
596 #remove any which are configured. At the end,
597 #what's left in existing is the set of loggers
598 #which were in the previous configuration but
599 #which are not in the new configuration.
600 root = logging.root
601 existing = list(root.manager.loggerDict.keys())
602 #The list needs to be sorted so that we can
603 #avoid disabling child loggers of explicitly
604 #named loggers. With a sorted list it is easier
605 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100606 existing.sort()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000607 #We'll keep the list of existing loggers
608 #which are children of named loggers here...
609 child_loggers = []
610 #now set up the new ones...
611 loggers = config.get('loggers', EMPTY_DICT)
612 for name in loggers:
613 if name in existing:
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000614 i = existing.index(name) + 1 # look after name
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000615 prefixed = name + "."
616 pflen = len(prefixed)
617 num_existing = len(existing)
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000618 while i < num_existing:
619 if existing[i][:pflen] == prefixed:
620 child_loggers.append(existing[i])
621 i += 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000622 existing.remove(name)
623 try:
624 self.configure_logger(name, loggers[name])
625 except Exception as e:
626 raise ValueError('Unable to configure logger '
627 '%r: %s' % (name, e))
628
629 #Disable any old loggers. There's no point deleting
630 #them as other threads may continue to hold references
631 #and by disabling them, you stop them doing any logging.
632 #However, don't disable children of named loggers, as that's
633 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000634 #for log in existing:
635 # logger = root.manager.loggerDict[log]
636 # if log in child_loggers:
637 # logger.level = logging.NOTSET
638 # logger.handlers = []
639 # logger.propagate = True
640 # elif disable_existing:
641 # logger.disabled = True
642 _handle_existing_loggers(existing, child_loggers,
643 disable_existing)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000644
645 # And finally, do the root logger
646 root = config.get('root', None)
647 if root:
648 try:
649 self.configure_root(root)
650 except Exception as e:
651 raise ValueError('Unable to configure root '
652 'logger: %s' % e)
653 finally:
654 logging._releaseLock()
655
656 def configure_formatter(self, config):
657 """Configure a formatter from a dictionary."""
658 if '()' in config:
659 factory = config['()'] # for use in exception handler
660 try:
661 result = self.configure_custom(config)
662 except TypeError as te:
663 if "'format'" not in str(te):
664 raise
665 #Name of parameter changed from fmt to format.
666 #Retry with old name.
667 #This is so that code can be used with older Python versions
668 #(e.g. by Django)
669 config['fmt'] = config.pop('format')
670 config['()'] = factory
671 result = self.configure_custom(config)
672 else:
673 fmt = config.get('format', None)
674 dfmt = config.get('datefmt', None)
Vinay Sajip28421c62013-03-29 17:56:54 +0000675 style = config.get('style', '%')
676 result = logging.Formatter(fmt, dfmt, style)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000677 return result
678
679 def configure_filter(self, config):
680 """Configure a filter from a dictionary."""
681 if '()' in config:
682 result = self.configure_custom(config)
683 else:
684 name = config.get('name', '')
685 result = logging.Filter(name)
686 return result
687
688 def add_filters(self, filterer, filters):
689 """Add filters to a filterer from a list of names."""
690 for f in filters:
691 try:
692 filterer.addFilter(self.config['filters'][f])
693 except Exception as e:
694 raise ValueError('Unable to add filter %r: %s' % (f, e))
695
696 def configure_handler(self, config):
697 """Configure a handler from a dictionary."""
Vinay Sajip28421c62013-03-29 17:56:54 +0000698 config_copy = dict(config) # for restoring in case of error
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000699 formatter = config.pop('formatter', None)
700 if formatter:
701 try:
702 formatter = self.config['formatters'][formatter]
703 except Exception as e:
704 raise ValueError('Unable to set formatter '
705 '%r: %s' % (formatter, e))
706 level = config.pop('level', None)
707 filters = config.pop('filters', None)
708 if '()' in config:
709 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200710 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000711 c = self.resolve(c)
712 factory = c
713 else:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000714 cname = config.pop('class')
715 klass = self.resolve(cname)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000716 #Special case for handler which refers to another handler
717 if issubclass(klass, logging.handlers.MemoryHandler) and\
718 'target' in config:
719 try:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000720 th = self.config['handlers'][config['target']]
721 if not isinstance(th, logging.Handler):
Vinay Sajip28421c62013-03-29 17:56:54 +0000722 config.update(config_copy) # restore for deferred cfg
Vinay Sajip3f885b52013-03-22 15:19:54 +0000723 raise TypeError('target not configured yet')
724 config['target'] = th
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000725 except Exception as e:
726 raise ValueError('Unable to set target handler '
727 '%r: %s' % (config['target'], e))
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000728 elif issubclass(klass, logging.handlers.SMTPHandler) and\
729 'mailhost' in config:
730 config['mailhost'] = self.as_tuple(config['mailhost'])
731 elif issubclass(klass, logging.handlers.SysLogHandler) and\
732 'address' in config:
733 config['address'] = self.as_tuple(config['address'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000734 factory = klass
Vinay Sajip8d270232012-11-15 14:20:18 +0000735 props = config.pop('.', None)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000736 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
737 try:
738 result = factory(**kwargs)
739 except TypeError as te:
740 if "'stream'" not in str(te):
741 raise
742 #The argument name changed from strm to stream
743 #Retry with old name.
744 #This is so that code can be used with older Python versions
745 #(e.g. by Django)
746 kwargs['strm'] = kwargs.pop('stream')
747 result = factory(**kwargs)
748 if formatter:
749 result.setFormatter(formatter)
750 if level is not None:
751 result.setLevel(logging._checkLevel(level))
752 if filters:
753 self.add_filters(result, filters)
Vinay Sajip8d270232012-11-15 14:20:18 +0000754 if props:
755 for name, value in props.items():
756 setattr(result, name, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000757 return result
758
759 def add_handlers(self, logger, handlers):
760 """Add handlers to a logger from a list of names."""
761 for h in handlers:
762 try:
763 logger.addHandler(self.config['handlers'][h])
764 except Exception as e:
765 raise ValueError('Unable to add handler %r: %s' % (h, e))
766
767 def common_logger_config(self, logger, config, incremental=False):
768 """
769 Perform configuration which is common to root and non-root loggers.
770 """
771 level = config.get('level', None)
772 if level is not None:
773 logger.setLevel(logging._checkLevel(level))
774 if not incremental:
775 #Remove any existing handlers
776 for h in logger.handlers[:]:
777 logger.removeHandler(h)
778 handlers = config.get('handlers', None)
779 if handlers:
780 self.add_handlers(logger, handlers)
781 filters = config.get('filters', None)
782 if filters:
783 self.add_filters(logger, filters)
784
785 def configure_logger(self, name, config, incremental=False):
786 """Configure a non-root logger from a dictionary."""
787 logger = logging.getLogger(name)
788 self.common_logger_config(logger, config, incremental)
789 propagate = config.get('propagate', None)
790 if propagate is not None:
791 logger.propagate = propagate
792
793 def configure_root(self, config, incremental=False):
794 """Configure a root logger from a dictionary."""
795 root = logging.getLogger()
796 self.common_logger_config(root, config, incremental)
797
798dictConfigClass = DictConfigurator
799
800def dictConfig(config):
801 """Configure logging using a dictionary."""
802 dictConfigClass(config).configure()
803
804
Vinay Sajip4ded5512012-10-02 15:56:16 +0100805def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000806 """
807 Start up a socket server on the specified port, and listen for new
808 configurations.
809
810 These will be sent as a file suitable for processing by fileConfig().
811 Returns a Thread object on which you can call start() to start the server,
812 and which you can join() when appropriate. To stop the server, call
813 stopListening().
Vinay Sajip3e763da2012-10-02 16:15:33 +0100814
815 Use the ``verify`` argument to verify any bytes received across the wire
816 from a client. If specified, it should be a callable which receives a
817 single argument - the bytes of configuration data received across the
818 network - and it should return either ``None``, to indicate that the
819 passed in bytes could not be verified and should be discarded, or a
820 byte string which is then passed to the configuration machinery as
821 normal. Note that you can return transformed bytes, e.g. by decrypting
822 the bytes passed in.
Guido van Rossum57102f82002-11-13 16:15:58 +0000823 """
Vinay Sajip985ef872011-04-26 19:34:04 +0100824 if not thread: #pragma: no cover
Collin Winterce36ad82007-08-30 01:19:48 +0000825 raise NotImplementedError("listen() needs threading to work")
Guido van Rossum57102f82002-11-13 16:15:58 +0000826
827 class ConfigStreamHandler(StreamRequestHandler):
828 """
829 Handler for a logging configuration request.
830
831 It expects a completely new logging configuration and uses fileConfig
832 to install it.
833 """
834 def handle(self):
835 """
836 Handle a request.
837
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000838 Each request is expected to be a 4-byte length, packed using
839 struct.pack(">L", n), followed by the config file.
840 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000841 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000842 try:
843 conn = self.connection
844 chunk = conn.recv(4)
845 if len(chunk) == 4:
846 slen = struct.unpack(">L", chunk)[0]
847 chunk = self.connection.recv(slen)
848 while len(chunk) < slen:
849 chunk = chunk + conn.recv(slen - len(chunk))
Vinay Sajip4ded5512012-10-02 15:56:16 +0100850 if self.server.verify is not None:
851 chunk = self.server.verify(chunk)
852 if chunk is not None: # verified, can process
853 chunk = chunk.decode("utf-8")
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000854 try:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100855 import json
856 d =json.loads(chunk)
857 assert isinstance(d, dict)
858 dictConfig(d)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100859 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100860 #Apply new configuration.
861
862 file = io.StringIO(chunk)
863 try:
864 fileConfig(file)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100865 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100866 traceback.print_exc()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000867 if self.server.ready:
868 self.server.ready.set()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200869 except OSError as e:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000870 if not isinstance(e.args, tuple):
Guido van Rossum57102f82002-11-13 16:15:58 +0000871 raise
872 else:
873 errcode = e.args[0]
874 if errcode != RESET_ERROR:
875 raise
876
877 class ConfigSocketReceiver(ThreadingTCPServer):
878 """
879 A simple TCP socket-based logging config receiver.
880 """
881
882 allow_reuse_address = 1
883
884 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100885 handler=None, ready=None, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000886 ThreadingTCPServer.__init__(self, (host, port), handler)
887 logging._acquireLock()
888 self.abort = 0
889 logging._releaseLock()
890 self.timeout = 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000891 self.ready = ready
Vinay Sajip4ded5512012-10-02 15:56:16 +0100892 self.verify = verify
Guido van Rossum57102f82002-11-13 16:15:58 +0000893
894 def serve_until_stopped(self):
895 import select
896 abort = 0
897 while not abort:
898 rd, wr, ex = select.select([self.socket.fileno()],
899 [], [],
900 self.timeout)
901 if rd:
902 self.handle_request()
903 logging._acquireLock()
904 abort = self.abort
905 logging._releaseLock()
Brian Curtin6ff2a7d2010-10-31 04:40:53 +0000906 self.socket.close()
Guido van Rossum57102f82002-11-13 16:15:58 +0000907
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000908 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000909
Vinay Sajip4ded5512012-10-02 15:56:16 +0100910 def __init__(self, rcvr, hdlr, port, verify):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000911 super(Server, self).__init__()
912 self.rcvr = rcvr
913 self.hdlr = hdlr
914 self.port = port
Vinay Sajip4ded5512012-10-02 15:56:16 +0100915 self.verify = verify
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000916 self.ready = threading.Event()
917
918 def run(self):
919 server = self.rcvr(port=self.port, handler=self.hdlr,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100920 ready=self.ready,
921 verify=self.verify)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000922 if self.port == 0:
923 self.port = server.server_address[1]
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000924 self.ready.set()
925 global _listener
926 logging._acquireLock()
927 _listener = server
928 logging._releaseLock()
929 server.serve_until_stopped()
930
Vinay Sajip4ded5512012-10-02 15:56:16 +0100931 return Server(ConfigSocketReceiver, ConfigStreamHandler, port, verify)
Guido van Rossum57102f82002-11-13 16:15:58 +0000932
933def stopListening():
934 """
935 Stop the listening server which was created with a call to listen().
936 """
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000937 global _listener
Vinay Sajip9fdd11b2010-09-25 17:48:25 +0000938 logging._acquireLock()
939 try:
940 if _listener:
941 _listener.abort = 1
942 _listener = None
943 finally:
Guido van Rossum57102f82002-11-13 16:15:58 +0000944 logging._releaseLock()