blob: 5afdf9f8d73b19351fb82eb50b6f4d7ab950d41d [file] [log] [blame]
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001# Copyright 2001-2010 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 Sajipdb81c4c2010-02-25 23:13:06 +000022Copyright (C) 2001-2010 Vinay Sajip. All Rights Reserved.
Guido van Rossum57102f82002-11-13 16:15:58 +000023
24To use, simply 'import logging' and log away!
25"""
26
Vinay Sajipdb81c4c2010-02-25 23:13:06 +000027import sys, logging, logging.handlers, socket, struct, os, traceback, re
28import types, 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
33except ImportError:
34 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
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000101def _encoded(s):
102 return s if isinstance(s, str) else s.encode('utf-8')
103
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.
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000218 existing.sort(key=_encoded)
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:
Christian Heimes96f31632007-11-12 01:32:03 +0000229 i = existing.index(qn)
230 prefixed = qn + "."
231 pflen = len(prefixed)
232 num_existing = len(existing)
233 i = i + 1 # look at the entry after qn
234 while (i < num_existing) and (existing[i][:pflen] == prefixed):
235 child_loggers.append(existing[i])
236 i = 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('()')
474 if not hasattr(c, '__call__'):
475 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.
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000591 existing.sort(key=_encoded)
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:
599 i = existing.index(name)
600 prefixed = name + "."
601 pflen = len(prefixed)
602 num_existing = len(existing)
603 i = i + 1 # look at the entry after name
604 while (i < num_existing) and\
605 (existing[i][:pflen] == prefixed):
606 child_loggers.append(existing[i])
607 i = i + 1
608 existing.remove(name)
609 try:
610 self.configure_logger(name, loggers[name])
611 except Exception as e:
612 raise ValueError('Unable to configure logger '
613 '%r: %s' % (name, e))
614
615 #Disable any old loggers. There's no point deleting
616 #them as other threads may continue to hold references
617 #and by disabling them, you stop them doing any logging.
618 #However, don't disable children of named loggers, as that's
619 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000620 #for log in existing:
621 # logger = root.manager.loggerDict[log]
622 # if log in child_loggers:
623 # logger.level = logging.NOTSET
624 # logger.handlers = []
625 # logger.propagate = True
626 # elif disable_existing:
627 # logger.disabled = True
628 _handle_existing_loggers(existing, child_loggers,
629 disable_existing)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000630
631 # And finally, do the root logger
632 root = config.get('root', None)
633 if root:
634 try:
635 self.configure_root(root)
636 except Exception as e:
637 raise ValueError('Unable to configure root '
638 'logger: %s' % e)
639 finally:
640 logging._releaseLock()
641
642 def configure_formatter(self, config):
643 """Configure a formatter from a dictionary."""
644 if '()' in config:
645 factory = config['()'] # for use in exception handler
646 try:
647 result = self.configure_custom(config)
648 except TypeError as te:
649 if "'format'" not in str(te):
650 raise
651 #Name of parameter changed from fmt to format.
652 #Retry with old name.
653 #This is so that code can be used with older Python versions
654 #(e.g. by Django)
655 config['fmt'] = config.pop('format')
656 config['()'] = factory
657 result = self.configure_custom(config)
658 else:
659 fmt = config.get('format', None)
660 dfmt = config.get('datefmt', None)
661 result = logging.Formatter(fmt, dfmt)
662 return result
663
664 def configure_filter(self, config):
665 """Configure a filter from a dictionary."""
666 if '()' in config:
667 result = self.configure_custom(config)
668 else:
669 name = config.get('name', '')
670 result = logging.Filter(name)
671 return result
672
673 def add_filters(self, filterer, filters):
674 """Add filters to a filterer from a list of names."""
675 for f in filters:
676 try:
677 filterer.addFilter(self.config['filters'][f])
678 except Exception as e:
679 raise ValueError('Unable to add filter %r: %s' % (f, e))
680
681 def configure_handler(self, config):
682 """Configure a handler from a dictionary."""
683 formatter = config.pop('formatter', None)
684 if formatter:
685 try:
686 formatter = self.config['formatters'][formatter]
687 except Exception as e:
688 raise ValueError('Unable to set formatter '
689 '%r: %s' % (formatter, e))
690 level = config.pop('level', None)
691 filters = config.pop('filters', None)
692 if '()' in config:
693 c = config.pop('()')
694 if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
695 c = self.resolve(c)
696 factory = c
697 else:
698 klass = self.resolve(config.pop('class'))
699 #Special case for handler which refers to another handler
700 if issubclass(klass, logging.handlers.MemoryHandler) and\
701 'target' in config:
702 try:
703 config['target'] = self.config['handlers'][config['target']]
704 except Exception as e:
705 raise ValueError('Unable to set target handler '
706 '%r: %s' % (config['target'], e))
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000707 elif issubclass(klass, logging.handlers.SMTPHandler) and\
708 'mailhost' in config:
709 config['mailhost'] = self.as_tuple(config['mailhost'])
710 elif issubclass(klass, logging.handlers.SysLogHandler) and\
711 'address' in config:
712 config['address'] = self.as_tuple(config['address'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000713 factory = klass
714 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
715 try:
716 result = factory(**kwargs)
717 except TypeError as te:
718 if "'stream'" not in str(te):
719 raise
720 #The argument name changed from strm to stream
721 #Retry with old name.
722 #This is so that code can be used with older Python versions
723 #(e.g. by Django)
724 kwargs['strm'] = kwargs.pop('stream')
725 result = factory(**kwargs)
726 if formatter:
727 result.setFormatter(formatter)
728 if level is not None:
729 result.setLevel(logging._checkLevel(level))
730 if filters:
731 self.add_filters(result, filters)
732 return result
733
734 def add_handlers(self, logger, handlers):
735 """Add handlers to a logger from a list of names."""
736 for h in handlers:
737 try:
738 logger.addHandler(self.config['handlers'][h])
739 except Exception as e:
740 raise ValueError('Unable to add handler %r: %s' % (h, e))
741
742 def common_logger_config(self, logger, config, incremental=False):
743 """
744 Perform configuration which is common to root and non-root loggers.
745 """
746 level = config.get('level', None)
747 if level is not None:
748 logger.setLevel(logging._checkLevel(level))
749 if not incremental:
750 #Remove any existing handlers
751 for h in logger.handlers[:]:
752 logger.removeHandler(h)
753 handlers = config.get('handlers', None)
754 if handlers:
755 self.add_handlers(logger, handlers)
756 filters = config.get('filters', None)
757 if filters:
758 self.add_filters(logger, filters)
759
760 def configure_logger(self, name, config, incremental=False):
761 """Configure a non-root logger from a dictionary."""
762 logger = logging.getLogger(name)
763 self.common_logger_config(logger, config, incremental)
764 propagate = config.get('propagate', None)
765 if propagate is not None:
766 logger.propagate = propagate
767
768 def configure_root(self, config, incremental=False):
769 """Configure a root logger from a dictionary."""
770 root = logging.getLogger()
771 self.common_logger_config(root, config, incremental)
772
773dictConfigClass = DictConfigurator
774
775def dictConfig(config):
776 """Configure logging using a dictionary."""
777 dictConfigClass(config).configure()
778
779
Guido van Rossum57102f82002-11-13 16:15:58 +0000780def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
781 """
782 Start up a socket server on the specified port, and listen for new
783 configurations.
784
785 These will be sent as a file suitable for processing by fileConfig().
786 Returns a Thread object on which you can call start() to start the server,
787 and which you can join() when appropriate. To stop the server, call
788 stopListening().
789 """
790 if not thread:
Collin Winterce36ad82007-08-30 01:19:48 +0000791 raise NotImplementedError("listen() needs threading to work")
Guido van Rossum57102f82002-11-13 16:15:58 +0000792
793 class ConfigStreamHandler(StreamRequestHandler):
794 """
795 Handler for a logging configuration request.
796
797 It expects a completely new logging configuration and uses fileConfig
798 to install it.
799 """
800 def handle(self):
801 """
802 Handle a request.
803
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000804 Each request is expected to be a 4-byte length, packed using
805 struct.pack(">L", n), followed by the config file.
806 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000807 """
808 import tempfile
809 try:
810 conn = self.connection
811 chunk = conn.recv(4)
812 if len(chunk) == 4:
813 slen = struct.unpack(">L", chunk)[0]
814 chunk = self.connection.recv(slen)
815 while len(chunk) < slen:
816 chunk = chunk + conn.recv(slen - len(chunk))
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000817 chunk = chunk.decode("utf-8")
Vinay Sajip989b69a2006-01-16 21:28:37 +0000818 try:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000819 import json
820 d =json.loads(chunk)
821 assert isinstance(d, dict)
822 dictConfig(d)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000823 except:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000824 #Apply new configuration.
825
826 file = io.StringIO(chunk)
827 try:
828 fileConfig(file)
829 except (KeyboardInterrupt, SystemExit):
830 raise
831 except:
832 traceback.print_exc()
833 if self.server.ready:
834 self.server.ready.set()
Guido van Rossumb940e112007-01-10 16:19:56 +0000835 except socket.error as e:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000836 if not isinstance(e.args, tuple):
Guido van Rossum57102f82002-11-13 16:15:58 +0000837 raise
838 else:
839 errcode = e.args[0]
840 if errcode != RESET_ERROR:
841 raise
842
843 class ConfigSocketReceiver(ThreadingTCPServer):
844 """
845 A simple TCP socket-based logging config receiver.
846 """
847
848 allow_reuse_address = 1
849
850 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000851 handler=None, ready=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000852 ThreadingTCPServer.__init__(self, (host, port), handler)
853 logging._acquireLock()
854 self.abort = 0
855 logging._releaseLock()
856 self.timeout = 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000857 self.ready = ready
Guido van Rossum57102f82002-11-13 16:15:58 +0000858
859 def serve_until_stopped(self):
860 import select
861 abort = 0
862 while not abort:
863 rd, wr, ex = select.select([self.socket.fileno()],
864 [], [],
865 self.timeout)
866 if rd:
867 self.handle_request()
868 logging._acquireLock()
869 abort = self.abort
870 logging._releaseLock()
Brian Curtin6ff2a7d2010-10-31 04:40:53 +0000871 self.socket.close()
Guido van Rossum57102f82002-11-13 16:15:58 +0000872
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000873 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000874
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000875 def __init__(self, rcvr, hdlr, port):
876 super(Server, self).__init__()
877 self.rcvr = rcvr
878 self.hdlr = hdlr
879 self.port = port
880 self.ready = threading.Event()
881
882 def run(self):
883 server = self.rcvr(port=self.port, handler=self.hdlr,
884 ready=self.ready)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000885 if self.port == 0:
886 self.port = server.server_address[1]
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000887 self.ready.set()
888 global _listener
889 logging._acquireLock()
890 _listener = server
891 logging._releaseLock()
892 server.serve_until_stopped()
893
894 return Server(ConfigSocketReceiver, ConfigStreamHandler, port)
Guido van Rossum57102f82002-11-13 16:15:58 +0000895
896def stopListening():
897 """
898 Stop the listening server which was created with a call to listen().
899 """
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000900 global _listener
Vinay Sajip9fdd11b2010-09-25 17:48:25 +0000901 logging._acquireLock()
902 try:
903 if _listener:
904 _listener.abort = 1
905 _listener = None
906 finally:
Guido van Rossum57102f82002-11-13 16:15:58 +0000907 logging._releaseLock()