blob: 0694d21fe05e0c8d960efc3c3b7fa8cc90da0e99 [file] [log] [blame]
Vinay Sajip3e763da2012-10-02 16:15:33 +01001# Copyright 2001-2012 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 Sajip3e763da2012-10-02 16:15:33 +010022Copyright (C) 2001-2012 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
178 elif disable_existing:
179 logger.disabled = True
180
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)
567 for name in sorted(handlers):
568 try:
569 handler = self.configure_handler(handlers[name])
570 handler.name = name
571 handlers[name] = handler
572 except Exception as e:
573 raise ValueError('Unable to configure handler '
574 '%r: %s' % (name, e))
575 # 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 '
612 '%r: %s' % (name, e))
613
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 '
637 'logger: %s' % e)
638 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)
660 result = logging.Formatter(fmt, dfmt)
661 return result
662
663 def configure_filter(self, config):
664 """Configure a filter from a dictionary."""
665 if '()' in config:
666 result = self.configure_custom(config)
667 else:
668 name = config.get('name', '')
669 result = logging.Filter(name)
670 return result
671
672 def add_filters(self, filterer, filters):
673 """Add filters to a filterer from a list of names."""
674 for f in filters:
675 try:
676 filterer.addFilter(self.config['filters'][f])
677 except Exception as e:
678 raise ValueError('Unable to add filter %r: %s' % (f, e))
679
680 def configure_handler(self, config):
681 """Configure a handler from a dictionary."""
682 formatter = config.pop('formatter', None)
683 if formatter:
684 try:
685 formatter = self.config['formatters'][formatter]
686 except Exception as e:
687 raise ValueError('Unable to set formatter '
688 '%r: %s' % (formatter, e))
689 level = config.pop('level', None)
690 filters = config.pop('filters', None)
691 if '()' in config:
692 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200693 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000694 c = self.resolve(c)
695 factory = c
696 else:
697 klass = self.resolve(config.pop('class'))
698 #Special case for handler which refers to another handler
699 if issubclass(klass, logging.handlers.MemoryHandler) and\
700 'target' in config:
701 try:
702 config['target'] = self.config['handlers'][config['target']]
703 except Exception as e:
704 raise ValueError('Unable to set target handler '
705 '%r: %s' % (config['target'], e))
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000706 elif issubclass(klass, logging.handlers.SMTPHandler) and\
707 'mailhost' in config:
708 config['mailhost'] = self.as_tuple(config['mailhost'])
709 elif issubclass(klass, logging.handlers.SysLogHandler) and\
710 'address' in config:
711 config['address'] = self.as_tuple(config['address'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000712 factory = klass
713 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
714 try:
715 result = factory(**kwargs)
716 except TypeError as te:
717 if "'stream'" not in str(te):
718 raise
719 #The argument name changed from strm to stream
720 #Retry with old name.
721 #This is so that code can be used with older Python versions
722 #(e.g. by Django)
723 kwargs['strm'] = kwargs.pop('stream')
724 result = factory(**kwargs)
725 if formatter:
726 result.setFormatter(formatter)
727 if level is not None:
728 result.setLevel(logging._checkLevel(level))
729 if filters:
730 self.add_filters(result, filters)
731 return result
732
733 def add_handlers(self, logger, handlers):
734 """Add handlers to a logger from a list of names."""
735 for h in handlers:
736 try:
737 logger.addHandler(self.config['handlers'][h])
738 except Exception as e:
739 raise ValueError('Unable to add handler %r: %s' % (h, e))
740
741 def common_logger_config(self, logger, config, incremental=False):
742 """
743 Perform configuration which is common to root and non-root loggers.
744 """
745 level = config.get('level', None)
746 if level is not None:
747 logger.setLevel(logging._checkLevel(level))
748 if not incremental:
749 #Remove any existing handlers
750 for h in logger.handlers[:]:
751 logger.removeHandler(h)
752 handlers = config.get('handlers', None)
753 if handlers:
754 self.add_handlers(logger, handlers)
755 filters = config.get('filters', None)
756 if filters:
757 self.add_filters(logger, filters)
758
759 def configure_logger(self, name, config, incremental=False):
760 """Configure a non-root logger from a dictionary."""
761 logger = logging.getLogger(name)
762 self.common_logger_config(logger, config, incremental)
763 propagate = config.get('propagate', None)
764 if propagate is not None:
765 logger.propagate = propagate
766
767 def configure_root(self, config, incremental=False):
768 """Configure a root logger from a dictionary."""
769 root = logging.getLogger()
770 self.common_logger_config(root, config, incremental)
771
772dictConfigClass = DictConfigurator
773
774def dictConfig(config):
775 """Configure logging using a dictionary."""
776 dictConfigClass(config).configure()
777
778
Vinay Sajip4ded5512012-10-02 15:56:16 +0100779def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000780 """
781 Start up a socket server on the specified port, and listen for new
782 configurations.
783
784 These will be sent as a file suitable for processing by fileConfig().
785 Returns a Thread object on which you can call start() to start the server,
786 and which you can join() when appropriate. To stop the server, call
787 stopListening().
Vinay Sajip3e763da2012-10-02 16:15:33 +0100788
789 Use the ``verify`` argument to verify any bytes received across the wire
790 from a client. If specified, it should be a callable which receives a
791 single argument - the bytes of configuration data received across the
792 network - and it should return either ``None``, to indicate that the
793 passed in bytes could not be verified and should be discarded, or a
794 byte string which is then passed to the configuration machinery as
795 normal. Note that you can return transformed bytes, e.g. by decrypting
796 the bytes passed in.
Guido van Rossum57102f82002-11-13 16:15:58 +0000797 """
Vinay Sajip985ef872011-04-26 19:34:04 +0100798 if not thread: #pragma: no cover
Collin Winterce36ad82007-08-30 01:19:48 +0000799 raise NotImplementedError("listen() needs threading to work")
Guido van Rossum57102f82002-11-13 16:15:58 +0000800
801 class ConfigStreamHandler(StreamRequestHandler):
802 """
803 Handler for a logging configuration request.
804
805 It expects a completely new logging configuration and uses fileConfig
806 to install it.
807 """
808 def handle(self):
809 """
810 Handle a request.
811
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000812 Each request is expected to be a 4-byte length, packed using
813 struct.pack(">L", n), followed by the config file.
814 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000815 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000816 try:
817 conn = self.connection
818 chunk = conn.recv(4)
819 if len(chunk) == 4:
820 slen = struct.unpack(">L", chunk)[0]
821 chunk = self.connection.recv(slen)
822 while len(chunk) < slen:
823 chunk = chunk + conn.recv(slen - len(chunk))
Vinay Sajip4ded5512012-10-02 15:56:16 +0100824 if self.server.verify is not None:
825 chunk = self.server.verify(chunk)
826 if chunk is not None: # verified, can process
827 chunk = chunk.decode("utf-8")
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000828 try:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100829 import json
830 d =json.loads(chunk)
831 assert isinstance(d, dict)
832 dictConfig(d)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100833 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100834 #Apply new configuration.
835
836 file = io.StringIO(chunk)
837 try:
838 fileConfig(file)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100839 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100840 traceback.print_exc()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000841 if self.server.ready:
842 self.server.ready.set()
Guido van Rossumb940e112007-01-10 16:19:56 +0000843 except socket.error as e:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000844 if not isinstance(e.args, tuple):
Guido van Rossum57102f82002-11-13 16:15:58 +0000845 raise
846 else:
847 errcode = e.args[0]
848 if errcode != RESET_ERROR:
849 raise
850
851 class ConfigSocketReceiver(ThreadingTCPServer):
852 """
853 A simple TCP socket-based logging config receiver.
854 """
855
856 allow_reuse_address = 1
857
858 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100859 handler=None, ready=None, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000860 ThreadingTCPServer.__init__(self, (host, port), handler)
861 logging._acquireLock()
862 self.abort = 0
863 logging._releaseLock()
864 self.timeout = 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000865 self.ready = ready
Vinay Sajip4ded5512012-10-02 15:56:16 +0100866 self.verify = verify
Guido van Rossum57102f82002-11-13 16:15:58 +0000867
868 def serve_until_stopped(self):
869 import select
870 abort = 0
871 while not abort:
872 rd, wr, ex = select.select([self.socket.fileno()],
873 [], [],
874 self.timeout)
875 if rd:
876 self.handle_request()
877 logging._acquireLock()
878 abort = self.abort
879 logging._releaseLock()
Brian Curtin6ff2a7d2010-10-31 04:40:53 +0000880 self.socket.close()
Guido van Rossum57102f82002-11-13 16:15:58 +0000881
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000882 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000883
Vinay Sajip4ded5512012-10-02 15:56:16 +0100884 def __init__(self, rcvr, hdlr, port, verify):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000885 super(Server, self).__init__()
886 self.rcvr = rcvr
887 self.hdlr = hdlr
888 self.port = port
Vinay Sajip4ded5512012-10-02 15:56:16 +0100889 self.verify = verify
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000890 self.ready = threading.Event()
891
892 def run(self):
893 server = self.rcvr(port=self.port, handler=self.hdlr,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100894 ready=self.ready,
895 verify=self.verify)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000896 if self.port == 0:
897 self.port = server.server_address[1]
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000898 self.ready.set()
899 global _listener
900 logging._acquireLock()
901 _listener = server
902 logging._releaseLock()
903 server.serve_until_stopped()
904
Vinay Sajip4ded5512012-10-02 15:56:16 +0100905 return Server(ConfigSocketReceiver, ConfigStreamHandler, port, verify)
Guido van Rossum57102f82002-11-13 16:15:58 +0000906
907def stopListening():
908 """
909 Stop the listening server which was created with a call to listen().
910 """
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000911 global _listener
Vinay Sajip9fdd11b2010-09-25 17:48:25 +0000912 logging._acquireLock()
913 try:
914 if _listener:
915 _listener.abort = 1
916 _listener = None
917 finally:
Guido van Rossum57102f82002-11-13 16:15:58 +0000918 logging._releaseLock()