blob: 188061449d1d6c984555ac39725b3803479ac78e [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
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +000064 cp = configparser.ConfigParser(defaults)
Georg Brandl01e4d572010-02-06 22:27:51 +000065 if hasattr(fname, 'readline'):
Florent Xiclunadc692742010-08-15 20:16:27 +000066 cp.read_file(fname)
Guido van Rossum57102f82002-11-13 16:15:58 +000067 else:
68 cp.read(fname)
Vinay Sajip989b69a2006-01-16 21:28:37 +000069
70 formatters = _create_formatters(cp)
71
72 # critical section
Guido van Rossum57102f82002-11-13 16:15:58 +000073 logging._acquireLock()
74 try:
Vinay Sajip989b69a2006-01-16 21:28:37 +000075 logging._handlers.clear()
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000076 del logging._handlerList[:]
Vinay Sajip989b69a2006-01-16 21:28:37 +000077 # Handlers add themselves to logging._handlers
78 handlers = _install_handlers(cp, formatters)
Benjamin Petersonfea6a942008-07-02 16:11:42 +000079 _install_loggers(cp, handlers, disable_existing_loggers)
Guido van Rossum57102f82002-11-13 16:15:58 +000080 finally:
81 logging._releaseLock()
82
Vinay Sajip989b69a2006-01-16 21:28:37 +000083
Vinay Sajip7a7160b2006-01-20 18:28:03 +000084def _resolve(name):
85 """Resolve a dotted name to a global object."""
Neal Norwitz9d72bb42007-04-17 08:48:32 +000086 name = name.split('.')
Vinay Sajip7a7160b2006-01-20 18:28:03 +000087 used = name.pop(0)
88 found = __import__(used)
89 for n in name:
90 used = used + '.' + n
91 try:
92 found = getattr(found, n)
93 except AttributeError:
94 __import__(used)
95 found = getattr(found, n)
96 return found
97
Benjamin Petersonae5360b2008-09-08 23:05:23 +000098def _strip_spaces(alist):
99 return map(lambda x: x.strip(), alist)
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000100
Vinay Sajip989b69a2006-01-16 21:28:37 +0000101def _create_formatters(cp):
102 """Create and return formatters"""
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000103 flist = cp["formatters"]["keys"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000104 if not len(flist):
105 return {}
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000106 flist = flist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000107 flist = _strip_spaces(flist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000108 formatters = {}
109 for form in flist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000110 sectname = "formatter_%s" % form
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000111 fs = cp.get(sectname, "format", raw=True, fallback=None)
112 dfs = cp.get(sectname, "datefmt", raw=True, fallback=None)
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000113 c = logging.Formatter
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000114 class_name = cp[sectname].get("class")
115 if class_name:
116 c = _resolve(class_name)
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000117 f = c(fs, dfs)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000118 formatters[form] = f
119 return formatters
120
121
122def _install_handlers(cp, formatters):
123 """Install and return handlers"""
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000124 hlist = cp["handlers"]["keys"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000125 if not len(hlist):
126 return {}
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000127 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000128 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000129 handlers = {}
130 fixups = [] #for inter-handler references
131 for hand in hlist:
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000132 section = cp["handler_%s" % hand]
133 klass = section["class"]
134 fmt = section.get("formatter", "")
Georg Brandl3dbca812008-07-23 16:10:53 +0000135 try:
136 klass = eval(klass, vars(logging))
137 except (AttributeError, NameError):
138 klass = _resolve(klass)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000139 args = section["args"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000140 args = eval(args, vars(logging))
Neal Norwitzd9108552006-03-17 08:00:19 +0000141 h = klass(*args)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000142 if "level" in section:
143 level = section["level"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000144 h.setLevel(logging._levelNames[level])
145 if len(fmt):
146 h.setFormatter(formatters[fmt])
Benjamin Peterson41181742008-07-02 20:22:54 +0000147 if issubclass(klass, logging.handlers.MemoryHandler):
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000148 target = section.get("target", "")
Vinay Sajip989b69a2006-01-16 21:28:37 +0000149 if len(target): #the target handler may not be loaded yet, so keep for later...
150 fixups.append((h, target))
151 handlers[hand] = h
152 #now all handlers are loaded, fixup inter-handler references...
153 for h, t in fixups:
154 h.setTarget(handlers[t])
155 return handlers
156
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000157def _handle_existing_loggers(existing, child_loggers, disable_existing):
158 """
159 When (re)configuring logging, handle loggers which were in the previous
160 configuration but are not in the new configuration. There's no point
161 deleting them as other threads may continue to hold references to them;
162 and by disabling them, you stop them doing any logging.
Vinay Sajip989b69a2006-01-16 21:28:37 +0000163
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000164 However, don't disable children of named loggers, as that's probably not
165 what was intended by the user. Also, allow existing loggers to NOT be
166 disabled if disable_existing is false.
167 """
168 root = logging.root
169 for log in existing:
170 logger = root.manager.loggerDict[log]
171 if log in child_loggers:
172 logger.level = logging.NOTSET
173 logger.handlers = []
174 logger.propagate = True
Vinay Sajip68b4cc82013-03-23 11:18:45 +0000175 else:
176 logger.disabled = disable_existing
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000177
178def _install_loggers(cp, handlers, disable_existing):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000179 """Create and install loggers"""
180
181 # configure the root first
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000182 llist = cp["loggers"]["keys"]
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000183 llist = llist.split(",")
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000184 llist = list(map(lambda x: x.strip(), llist))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000185 llist.remove("root")
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000186 section = cp["logger_root"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000187 root = logging.root
188 log = root
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000189 if "level" in section:
190 level = section["level"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000191 log.setLevel(logging._levelNames[level])
192 for h in root.handlers[:]:
193 root.removeHandler(h)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000194 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000195 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000196 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000197 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000198 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000199 log.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000200
201 #and now the others...
202 #we don't want to lose the existing loggers,
203 #since other threads may have pointers to them.
204 #existing is set to contain all existing loggers,
205 #and as we go through the new configuration we
206 #remove any which are configured. At the end,
207 #what's left in existing is the set of loggers
208 #which were in the previous configuration but
209 #which are not in the new configuration.
Guido van Rossum8b8a5432007-02-12 00:07:01 +0000210 existing = list(root.manager.loggerDict.keys())
Christian Heimes96f31632007-11-12 01:32:03 +0000211 #The list needs to be sorted so that we can
212 #avoid disabling child loggers of explicitly
213 #named loggers. With a sorted list it is easier
214 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100215 existing.sort()
Christian Heimes96f31632007-11-12 01:32:03 +0000216 #We'll keep the list of existing loggers
217 #which are children of named loggers here...
218 child_loggers = []
Vinay Sajip989b69a2006-01-16 21:28:37 +0000219 #now set up the new ones...
220 for log in llist:
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000221 section = cp["logger_%s" % log]
222 qn = section["qualname"]
223 propagate = section.getint("propagate", fallback=1)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000224 logger = logging.getLogger(qn)
225 if qn in existing:
Vinay Sajip3f84b072011-03-07 17:49:33 +0000226 i = existing.index(qn) + 1 # start with the entry after qn
Christian Heimes96f31632007-11-12 01:32:03 +0000227 prefixed = qn + "."
228 pflen = len(prefixed)
229 num_existing = len(existing)
Vinay Sajip3f84b072011-03-07 17:49:33 +0000230 while i < num_existing:
231 if existing[i][:pflen] == prefixed:
232 child_loggers.append(existing[i])
233 i += 1
Vinay Sajip989b69a2006-01-16 21:28:37 +0000234 existing.remove(qn)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000235 if "level" in section:
236 level = section["level"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000237 logger.setLevel(logging._levelNames[level])
238 for h in logger.handlers[:]:
239 logger.removeHandler(h)
240 logger.propagate = propagate
241 logger.disabled = 0
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000242 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000243 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000244 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000245 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000246 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000247 logger.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000248
249 #Disable any old loggers. There's no point deleting
250 #them as other threads may continue to hold references
251 #and by disabling them, you stop them doing any logging.
Christian Heimes96f31632007-11-12 01:32:03 +0000252 #However, don't disable children of named loggers, as that's
253 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000254 #for log in existing:
255 # logger = root.manager.loggerDict[log]
256 # if log in child_loggers:
257 # logger.level = logging.NOTSET
258 # logger.handlers = []
259 # logger.propagate = 1
260 # elif disable_existing_loggers:
261 # logger.disabled = 1
262 _handle_existing_loggers(existing, child_loggers, disable_existing)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000263
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000264IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
265
266
267def valid_ident(s):
268 m = IDENTIFIER.match(s)
269 if not m:
270 raise ValueError('Not a valid Python identifier: %r' % s)
271 return True
272
273
274# The ConvertingXXX classes are wrappers around standard Python containers,
275# and they serve to convert any suitable values in the container. The
276# conversion converts base dicts, lists and tuples to their wrapped
277# equivalents, whereas strings which match a conversion format are converted
278# appropriately.
279#
280# Each wrapper should have a configurator attribute holding the actual
281# configurator to use for conversion.
282
283class ConvertingDict(dict):
284 """A converting dictionary wrapper."""
285
286 def __getitem__(self, key):
287 value = dict.__getitem__(self, key)
288 result = self.configurator.convert(value)
289 #If the converted value is different, save for next time
290 if value is not result:
291 self[key] = result
292 if type(result) in (ConvertingDict, ConvertingList,
293 ConvertingTuple):
294 result.parent = self
295 result.key = key
296 return result
297
298 def get(self, key, default=None):
299 value = dict.get(self, key, default)
300 result = self.configurator.convert(value)
301 #If the converted value is different, save for next time
302 if value is not result:
303 self[key] = result
304 if type(result) in (ConvertingDict, ConvertingList,
305 ConvertingTuple):
306 result.parent = self
307 result.key = key
308 return result
309
310 def pop(self, key, default=None):
311 value = dict.pop(self, key, default)
312 result = self.configurator.convert(value)
313 if value is not result:
314 if type(result) in (ConvertingDict, ConvertingList,
315 ConvertingTuple):
316 result.parent = self
317 result.key = key
318 return result
319
320class ConvertingList(list):
321 """A converting list wrapper."""
322 def __getitem__(self, key):
323 value = list.__getitem__(self, key)
324 result = self.configurator.convert(value)
325 #If the converted value is different, save for next time
326 if value is not result:
327 self[key] = result
328 if type(result) in (ConvertingDict, ConvertingList,
329 ConvertingTuple):
330 result.parent = self
331 result.key = key
332 return result
333
334 def pop(self, idx=-1):
335 value = list.pop(self, idx)
336 result = self.configurator.convert(value)
337 if value is not result:
338 if type(result) in (ConvertingDict, ConvertingList,
339 ConvertingTuple):
340 result.parent = self
341 return result
342
343class ConvertingTuple(tuple):
344 """A converting tuple wrapper."""
345 def __getitem__(self, key):
346 value = tuple.__getitem__(self, key)
347 result = self.configurator.convert(value)
348 if value is not result:
349 if type(result) in (ConvertingDict, ConvertingList,
350 ConvertingTuple):
351 result.parent = self
352 result.key = key
353 return result
354
355class BaseConfigurator(object):
356 """
357 The configurator base class which defines some useful defaults.
358 """
359
360 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
361
362 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
363 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
364 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
365 DIGIT_PATTERN = re.compile(r'^\d+$')
366
367 value_converters = {
368 'ext' : 'ext_convert',
369 'cfg' : 'cfg_convert',
370 }
371
372 # We might want to use a different one, e.g. importlib
Brett Cannonc2368502010-06-12 00:39:28 +0000373 importer = staticmethod(__import__)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000374
375 def __init__(self, config):
376 self.config = ConvertingDict(config)
377 self.config.configurator = self
378
379 def resolve(self, s):
380 """
381 Resolve strings to objects using standard import and attribute
382 syntax.
383 """
384 name = s.split('.')
385 used = name.pop(0)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000386 try:
387 found = self.importer(used)
388 for frag in name:
389 used += '.' + frag
390 try:
391 found = getattr(found, frag)
392 except AttributeError:
393 self.importer(used)
394 found = getattr(found, frag)
395 return found
396 except ImportError:
397 e, tb = sys.exc_info()[1:]
398 v = ValueError('Cannot resolve %r: %s' % (s, e))
399 v.__cause__, v.__traceback__ = e, tb
400 raise v
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000401
402 def ext_convert(self, value):
403 """Default converter for the ext:// protocol."""
404 return self.resolve(value)
405
406 def cfg_convert(self, value):
407 """Default converter for the cfg:// protocol."""
408 rest = value
409 m = self.WORD_PATTERN.match(rest)
410 if m is None:
411 raise ValueError("Unable to convert %r" % value)
412 else:
413 rest = rest[m.end():]
414 d = self.config[m.groups()[0]]
415 #print d, rest
416 while rest:
417 m = self.DOT_PATTERN.match(rest)
418 if m:
419 d = d[m.groups()[0]]
420 else:
421 m = self.INDEX_PATTERN.match(rest)
422 if m:
423 idx = m.groups()[0]
424 if not self.DIGIT_PATTERN.match(idx):
425 d = d[idx]
426 else:
427 try:
428 n = int(idx) # try as number first (most likely)
429 d = d[n]
430 except TypeError:
431 d = d[idx]
432 if m:
433 rest = rest[m.end():]
434 else:
435 raise ValueError('Unable to convert '
436 '%r at %r' % (value, rest))
437 #rest should be empty
438 return d
439
440 def convert(self, value):
441 """
442 Convert values to an appropriate type. dicts, lists and tuples are
443 replaced by their converting alternatives. Strings are checked to
444 see if they have a conversion format and are converted if they do.
445 """
446 if not isinstance(value, ConvertingDict) and isinstance(value, dict):
447 value = ConvertingDict(value)
448 value.configurator = self
449 elif not isinstance(value, ConvertingList) and isinstance(value, list):
450 value = ConvertingList(value)
451 value.configurator = self
452 elif not isinstance(value, ConvertingTuple) and\
453 isinstance(value, tuple):
454 value = ConvertingTuple(value)
455 value.configurator = self
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000456 elif isinstance(value, str): # str for py3k
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000457 m = self.CONVERT_PATTERN.match(value)
458 if m:
459 d = m.groupdict()
460 prefix = d['prefix']
461 converter = self.value_converters.get(prefix, None)
462 if converter:
463 suffix = d['suffix']
464 converter = getattr(self, converter)
465 value = converter(suffix)
466 return value
467
468 def configure_custom(self, config):
469 """Configure an object with a user-supplied factory."""
470 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200471 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000472 c = self.resolve(c)
473 props = config.pop('.', None)
474 # Check for valid identifiers
475 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
476 result = c(**kwargs)
477 if props:
478 for name, value in props.items():
479 setattr(result, name, value)
480 return result
481
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000482 def as_tuple(self, value):
483 """Utility function which converts lists to tuples."""
484 if isinstance(value, list):
485 value = tuple(value)
486 return value
487
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000488class DictConfigurator(BaseConfigurator):
489 """
490 Configure logging using a dictionary-like object to describe the
491 configuration.
492 """
493
494 def configure(self):
495 """Do the configuration."""
496
497 config = self.config
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000498 if 'version' not in config:
499 raise ValueError("dictionary doesn't specify a version")
500 if config['version'] != 1:
501 raise ValueError("Unsupported version: %s" % config['version'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000502 incremental = config.pop('incremental', False)
503 EMPTY_DICT = {}
504 logging._acquireLock()
505 try:
506 if incremental:
507 handlers = config.get('handlers', EMPTY_DICT)
508 for name in handlers:
509 if name not in logging._handlers:
510 raise ValueError('No handler found with '
511 'name %r' % name)
512 else:
513 try:
514 handler = logging._handlers[name]
515 handler_config = handlers[name]
516 level = handler_config.get('level', None)
517 if level:
518 handler.setLevel(logging._checkLevel(level))
519 except Exception as e:
520 raise ValueError('Unable to configure handler '
521 '%r: %s' % (name, e))
522 loggers = config.get('loggers', EMPTY_DICT)
523 for name in loggers:
524 try:
525 self.configure_logger(name, loggers[name], True)
526 except Exception as e:
527 raise ValueError('Unable to configure logger '
528 '%r: %s' % (name, e))
529 root = config.get('root', None)
530 if root:
531 try:
532 self.configure_root(root, True)
533 except Exception as e:
534 raise ValueError('Unable to configure root '
535 'logger: %s' % e)
536 else:
537 disable_existing = config.pop('disable_existing_loggers', True)
538
539 logging._handlers.clear()
540 del logging._handlerList[:]
541
542 # Do formatters first - they don't refer to anything else
543 formatters = config.get('formatters', EMPTY_DICT)
544 for name in formatters:
545 try:
546 formatters[name] = self.configure_formatter(
547 formatters[name])
548 except Exception as e:
549 raise ValueError('Unable to configure '
550 'formatter %r: %s' % (name, e))
551 # Next, do filters - they don't refer to anything else, either
552 filters = config.get('filters', EMPTY_DICT)
553 for name in filters:
554 try:
555 filters[name] = self.configure_filter(filters[name])
556 except Exception as e:
557 raise ValueError('Unable to configure '
558 'filter %r: %s' % (name, e))
559
560 # Next, do handlers - they refer to formatters and filters
561 # As handlers can refer to other handlers, sort the keys
562 # to allow a deterministic order of configuration
563 handlers = config.get('handlers', EMPTY_DICT)
Vinay Sajip3f885b52013-03-22 15:19:54 +0000564 deferred = []
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000565 for name in sorted(handlers):
566 try:
567 handler = self.configure_handler(handlers[name])
568 handler.name = name
569 handlers[name] = handler
570 except Exception as e:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000571 if 'target not configured yet' in str(e):
572 deferred.append(name)
573 else:
574 raise ValueError('Unable to configure handler '
575 '%r: %s' % (name, e))
576
577 # Now do any that were deferred
578 for name in deferred:
579 try:
580 handler = self.configure_handler(handlers[name])
581 handler.name = name
582 handlers[name] = handler
583 except Exception as e:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000584 raise ValueError('Unable to configure handler '
585 '%r: %s' % (name, e))
Vinay Sajip3f885b52013-03-22 15:19:54 +0000586
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000587 # Next, do loggers - they refer to handlers and filters
588
589 #we don't want to lose the existing loggers,
590 #since other threads may have pointers to them.
591 #existing is set to contain all existing loggers,
592 #and as we go through the new configuration we
593 #remove any which are configured. At the end,
594 #what's left in existing is the set of loggers
595 #which were in the previous configuration but
596 #which are not in the new configuration.
597 root = logging.root
598 existing = list(root.manager.loggerDict.keys())
599 #The list needs to be sorted so that we can
600 #avoid disabling child loggers of explicitly
601 #named loggers. With a sorted list it is easier
602 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100603 existing.sort()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000604 #We'll keep the list of existing loggers
605 #which are children of named loggers here...
606 child_loggers = []
607 #now set up the new ones...
608 loggers = config.get('loggers', EMPTY_DICT)
609 for name in loggers:
610 if name in existing:
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000611 i = existing.index(name) + 1 # look after name
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000612 prefixed = name + "."
613 pflen = len(prefixed)
614 num_existing = len(existing)
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000615 while i < num_existing:
616 if existing[i][:pflen] == prefixed:
617 child_loggers.append(existing[i])
618 i += 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000619 existing.remove(name)
620 try:
621 self.configure_logger(name, loggers[name])
622 except Exception as e:
623 raise ValueError('Unable to configure logger '
624 '%r: %s' % (name, e))
625
626 #Disable any old loggers. There's no point deleting
627 #them as other threads may continue to hold references
628 #and by disabling them, you stop them doing any logging.
629 #However, don't disable children of named loggers, as that's
630 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000631 #for log in existing:
632 # logger = root.manager.loggerDict[log]
633 # if log in child_loggers:
634 # logger.level = logging.NOTSET
635 # logger.handlers = []
636 # logger.propagate = True
637 # elif disable_existing:
638 # logger.disabled = True
639 _handle_existing_loggers(existing, child_loggers,
640 disable_existing)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000641
642 # And finally, do the root logger
643 root = config.get('root', None)
644 if root:
645 try:
646 self.configure_root(root)
647 except Exception as e:
648 raise ValueError('Unable to configure root '
649 'logger: %s' % e)
650 finally:
651 logging._releaseLock()
652
653 def configure_formatter(self, config):
654 """Configure a formatter from a dictionary."""
655 if '()' in config:
656 factory = config['()'] # for use in exception handler
657 try:
658 result = self.configure_custom(config)
659 except TypeError as te:
660 if "'format'" not in str(te):
661 raise
662 #Name of parameter changed from fmt to format.
663 #Retry with old name.
664 #This is so that code can be used with older Python versions
665 #(e.g. by Django)
666 config['fmt'] = config.pop('format')
667 config['()'] = factory
668 result = self.configure_custom(config)
669 else:
670 fmt = config.get('format', None)
671 dfmt = config.get('datefmt', None)
Vinay Sajip28421c62013-03-29 17:56:54 +0000672 style = config.get('style', '%')
673 result = logging.Formatter(fmt, dfmt, style)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000674 return result
675
676 def configure_filter(self, config):
677 """Configure a filter from a dictionary."""
678 if '()' in config:
679 result = self.configure_custom(config)
680 else:
681 name = config.get('name', '')
682 result = logging.Filter(name)
683 return result
684
685 def add_filters(self, filterer, filters):
686 """Add filters to a filterer from a list of names."""
687 for f in filters:
688 try:
689 filterer.addFilter(self.config['filters'][f])
690 except Exception as e:
691 raise ValueError('Unable to add filter %r: %s' % (f, e))
692
693 def configure_handler(self, config):
694 """Configure a handler from a dictionary."""
Vinay Sajip28421c62013-03-29 17:56:54 +0000695 config_copy = dict(config) # for restoring in case of error
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000696 formatter = config.pop('formatter', None)
697 if formatter:
698 try:
699 formatter = self.config['formatters'][formatter]
700 except Exception as e:
701 raise ValueError('Unable to set formatter '
702 '%r: %s' % (formatter, e))
703 level = config.pop('level', None)
704 filters = config.pop('filters', None)
705 if '()' in config:
706 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200707 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000708 c = self.resolve(c)
709 factory = c
710 else:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000711 cname = config.pop('class')
712 klass = self.resolve(cname)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000713 #Special case for handler which refers to another handler
714 if issubclass(klass, logging.handlers.MemoryHandler) and\
715 'target' in config:
716 try:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000717 th = self.config['handlers'][config['target']]
718 if not isinstance(th, logging.Handler):
Vinay Sajip28421c62013-03-29 17:56:54 +0000719 config.update(config_copy) # restore for deferred cfg
Vinay Sajip3f885b52013-03-22 15:19:54 +0000720 raise TypeError('target not configured yet')
721 config['target'] = th
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000722 except Exception as e:
723 raise ValueError('Unable to set target handler '
724 '%r: %s' % (config['target'], e))
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000725 elif issubclass(klass, logging.handlers.SMTPHandler) and\
726 'mailhost' in config:
727 config['mailhost'] = self.as_tuple(config['mailhost'])
728 elif issubclass(klass, logging.handlers.SysLogHandler) and\
729 'address' in config:
730 config['address'] = self.as_tuple(config['address'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000731 factory = klass
732 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
733 try:
734 result = factory(**kwargs)
735 except TypeError as te:
736 if "'stream'" not in str(te):
737 raise
738 #The argument name changed from strm to stream
739 #Retry with old name.
740 #This is so that code can be used with older Python versions
741 #(e.g. by Django)
742 kwargs['strm'] = kwargs.pop('stream')
743 result = factory(**kwargs)
744 if formatter:
745 result.setFormatter(formatter)
746 if level is not None:
747 result.setLevel(logging._checkLevel(level))
748 if filters:
749 self.add_filters(result, filters)
750 return result
751
752 def add_handlers(self, logger, handlers):
753 """Add handlers to a logger from a list of names."""
754 for h in handlers:
755 try:
756 logger.addHandler(self.config['handlers'][h])
757 except Exception as e:
758 raise ValueError('Unable to add handler %r: %s' % (h, e))
759
760 def common_logger_config(self, logger, config, incremental=False):
761 """
762 Perform configuration which is common to root and non-root loggers.
763 """
764 level = config.get('level', None)
765 if level is not None:
766 logger.setLevel(logging._checkLevel(level))
767 if not incremental:
768 #Remove any existing handlers
769 for h in logger.handlers[:]:
770 logger.removeHandler(h)
771 handlers = config.get('handlers', None)
772 if handlers:
773 self.add_handlers(logger, handlers)
774 filters = config.get('filters', None)
775 if filters:
776 self.add_filters(logger, filters)
777
778 def configure_logger(self, name, config, incremental=False):
779 """Configure a non-root logger from a dictionary."""
780 logger = logging.getLogger(name)
781 self.common_logger_config(logger, config, incremental)
782 propagate = config.get('propagate', None)
783 if propagate is not None:
784 logger.propagate = propagate
785
786 def configure_root(self, config, incremental=False):
787 """Configure a root logger from a dictionary."""
788 root = logging.getLogger()
789 self.common_logger_config(root, config, incremental)
790
791dictConfigClass = DictConfigurator
792
793def dictConfig(config):
794 """Configure logging using a dictionary."""
795 dictConfigClass(config).configure()
796
797
Guido van Rossum57102f82002-11-13 16:15:58 +0000798def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
799 """
800 Start up a socket server on the specified port, and listen for new
801 configurations.
802
803 These will be sent as a file suitable for processing by fileConfig().
804 Returns a Thread object on which you can call start() to start the server,
805 and which you can join() when appropriate. To stop the server, call
806 stopListening().
807 """
Vinay Sajip985ef872011-04-26 19:34:04 +0100808 if not thread: #pragma: no cover
Collin Winterce36ad82007-08-30 01:19:48 +0000809 raise NotImplementedError("listen() needs threading to work")
Guido van Rossum57102f82002-11-13 16:15:58 +0000810
811 class ConfigStreamHandler(StreamRequestHandler):
812 """
813 Handler for a logging configuration request.
814
815 It expects a completely new logging configuration and uses fileConfig
816 to install it.
817 """
818 def handle(self):
819 """
820 Handle a request.
821
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000822 Each request is expected to be a 4-byte length, packed using
823 struct.pack(">L", n), followed by the config file.
824 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000825 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000826 try:
827 conn = self.connection
828 chunk = conn.recv(4)
829 if len(chunk) == 4:
830 slen = struct.unpack(">L", chunk)[0]
831 chunk = self.connection.recv(slen)
832 while len(chunk) < slen:
833 chunk = chunk + conn.recv(slen - len(chunk))
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000834 chunk = chunk.decode("utf-8")
Vinay Sajip989b69a2006-01-16 21:28:37 +0000835 try:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000836 import json
837 d =json.loads(chunk)
838 assert isinstance(d, dict)
839 dictConfig(d)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000840 except:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000841 #Apply new configuration.
842
843 file = io.StringIO(chunk)
844 try:
845 fileConfig(file)
Vinay Sajip985ef872011-04-26 19:34:04 +0100846 except (KeyboardInterrupt, SystemExit): #pragma: no cover
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000847 raise
848 except:
849 traceback.print_exc()
850 if self.server.ready:
851 self.server.ready.set()
Guido van Rossumb940e112007-01-10 16:19:56 +0000852 except socket.error as e:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000853 if not isinstance(e.args, tuple):
Guido van Rossum57102f82002-11-13 16:15:58 +0000854 raise
855 else:
856 errcode = e.args[0]
857 if errcode != RESET_ERROR:
858 raise
859
860 class ConfigSocketReceiver(ThreadingTCPServer):
861 """
862 A simple TCP socket-based logging config receiver.
863 """
864
865 allow_reuse_address = 1
866
867 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000868 handler=None, ready=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000869 ThreadingTCPServer.__init__(self, (host, port), handler)
870 logging._acquireLock()
871 self.abort = 0
872 logging._releaseLock()
873 self.timeout = 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000874 self.ready = ready
Guido van Rossum57102f82002-11-13 16:15:58 +0000875
876 def serve_until_stopped(self):
877 import select
878 abort = 0
879 while not abort:
880 rd, wr, ex = select.select([self.socket.fileno()],
881 [], [],
882 self.timeout)
883 if rd:
884 self.handle_request()
885 logging._acquireLock()
886 abort = self.abort
887 logging._releaseLock()
Brian Curtin6ff2a7d2010-10-31 04:40:53 +0000888 self.socket.close()
Guido van Rossum57102f82002-11-13 16:15:58 +0000889
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000890 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000891
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000892 def __init__(self, rcvr, hdlr, port):
893 super(Server, self).__init__()
894 self.rcvr = rcvr
895 self.hdlr = hdlr
896 self.port = port
897 self.ready = threading.Event()
898
899 def run(self):
900 server = self.rcvr(port=self.port, handler=self.hdlr,
901 ready=self.ready)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000902 if self.port == 0:
903 self.port = server.server_address[1]
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000904 self.ready.set()
905 global _listener
906 logging._acquireLock()
907 _listener = server
908 logging._releaseLock()
909 server.serve_until_stopped()
910
911 return Server(ConfigSocketReceiver, ConfigStreamHandler, port)
Guido van Rossum57102f82002-11-13 16:15:58 +0000912
913def stopListening():
914 """
915 Stop the listening server which was created with a call to listen().
916 """
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000917 global _listener
Vinay Sajip9fdd11b2010-09-25 17:48:25 +0000918 logging._acquireLock()
919 try:
920 if _listener:
921 _listener.abort = 1
922 _listener = None
923 finally:
Guido van Rossum57102f82002-11-13 16:15:58 +0000924 logging._releaseLock()