blob: 1145e711903ce416ff56678121ceb759d0468b0f [file] [log] [blame]
Vinay Sajip28c382f2010-02-04 18:48:53 +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 Sajip28c382f2010-02-04 18:48:53 +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 Sajip28c382f2010-02-04 18:48:53 +000027import sys, logging, logging.handlers, socket, struct, os, traceback, re
28import types, cStringIO
Vinay Sajip612df8e2005-02-18 11:54:46 +000029
30try:
31 import thread
32 import threading
33except ImportError:
34 thread = None
Guido van Rossum57102f82002-11-13 16:15:58 +000035
Georg Brandle152a772008-05-24 18:31:28 +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
Vinay Sajip1c77b7f2009-10-10 20:32:36 +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).
61 In versions of ConfigParser which have the readfp method [typically
62 shipped in 2.x versions of Python], you can pass in a file-like object
63 rather than a filename, in which case the file-like object will be read
64 using readfp.
65 """
Georg Brandl392c6fc2008-05-25 07:25:25 +000066 import ConfigParser
Guido van Rossum57102f82002-11-13 16:15:58 +000067
Georg Brandl392c6fc2008-05-25 07:25:25 +000068 cp = ConfigParser.ConfigParser(defaults)
Guido van Rossum57102f82002-11-13 16:15:58 +000069 if hasattr(cp, 'readfp') and hasattr(fname, 'readline'):
70 cp.readfp(fname)
71 else:
72 cp.read(fname)
Vinay Sajip989b69a2006-01-16 21:28:37 +000073
74 formatters = _create_formatters(cp)
75
76 # critical section
Guido van Rossum57102f82002-11-13 16:15:58 +000077 logging._acquireLock()
78 try:
Vinay Sajip989b69a2006-01-16 21:28:37 +000079 logging._handlers.clear()
Georg Brandlf3e30422006-08-12 08:32:02 +000080 del logging._handlerList[:]
Vinay Sajip989b69a2006-01-16 21:28:37 +000081 # Handlers add themselves to logging._handlers
82 handlers = _install_handlers(cp, formatters)
Vinay Sajip5f7b97d2008-06-19 22:40:17 +000083 _install_loggers(cp, handlers, disable_existing_loggers)
Guido van Rossum57102f82002-11-13 16:15:58 +000084 finally:
85 logging._releaseLock()
86
Vinay Sajip989b69a2006-01-16 21:28:37 +000087
Vinay Sajip7a7160b2006-01-20 18:28:03 +000088def _resolve(name):
89 """Resolve a dotted name to a global object."""
Vinay Sajip1c77b7f2009-10-10 20:32:36 +000090 name = name.split('.')
Vinay Sajip7a7160b2006-01-20 18:28:03 +000091 used = name.pop(0)
92 found = __import__(used)
93 for n in name:
94 used = used + '.' + n
95 try:
96 found = getattr(found, n)
97 except AttributeError:
98 __import__(used)
99 found = getattr(found, n)
100 return found
101
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000102def _strip_spaces(alist):
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000103 return map(lambda x: x.strip(), alist)
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000104
Vinay Sajip989b69a2006-01-16 21:28:37 +0000105def _create_formatters(cp):
106 """Create and return formatters"""
107 flist = cp.get("formatters", "keys")
108 if not len(flist):
109 return {}
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000110 flist = flist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000111 flist = _strip_spaces(flist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000112 formatters = {}
113 for form in flist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000114 sectname = "formatter_%s" % form
Vinay Sajip989b69a2006-01-16 21:28:37 +0000115 opts = cp.options(sectname)
116 if "format" in opts:
117 fs = cp.get(sectname, "format", 1)
118 else:
119 fs = None
120 if "datefmt" in opts:
121 dfs = cp.get(sectname, "datefmt", 1)
122 else:
123 dfs = None
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000124 c = logging.Formatter
125 if "class" in opts:
126 class_name = cp.get(sectname, "class")
127 if class_name:
128 c = _resolve(class_name)
129 f = c(fs, dfs)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000130 formatters[form] = f
131 return formatters
132
133
134def _install_handlers(cp, formatters):
135 """Install and return handlers"""
136 hlist = cp.get("handlers", "keys")
137 if not len(hlist):
138 return {}
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000139 hlist = hlist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000140 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000141 handlers = {}
142 fixups = [] #for inter-handler references
143 for hand in hlist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000144 sectname = "handler_%s" % hand
Vinay Sajip989b69a2006-01-16 21:28:37 +0000145 klass = cp.get(sectname, "class")
146 opts = cp.options(sectname)
147 if "formatter" in opts:
148 fmt = cp.get(sectname, "formatter")
149 else:
150 fmt = ""
Vinay Sajipbc7e34f2008-07-18 08:59:06 +0000151 try:
152 klass = eval(klass, vars(logging))
153 except (AttributeError, NameError):
154 klass = _resolve(klass)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000155 args = cp.get(sectname, "args")
156 args = eval(args, vars(logging))
Brett Cannone6bfe802008-08-04 00:09:43 +0000157 h = klass(*args)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000158 if "level" in opts:
159 level = cp.get(sectname, "level")
160 h.setLevel(logging._levelNames[level])
161 if len(fmt):
162 h.setFormatter(formatters[fmt])
Vinay Sajip5ff71712008-06-29 21:25:28 +0000163 if issubclass(klass, logging.handlers.MemoryHandler):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000164 if "target" in opts:
165 target = cp.get(sectname,"target")
166 else:
167 target = ""
168 if len(target): #the target handler may not be loaded yet, so keep for later...
169 fixups.append((h, target))
170 handlers[hand] = h
171 #now all handlers are loaded, fixup inter-handler references...
172 for h, t in fixups:
173 h.setTarget(handlers[t])
174 return handlers
175
176
Vinay Sajip5f7b97d2008-06-19 22:40:17 +0000177def _install_loggers(cp, handlers, disable_existing_loggers):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000178 """Create and install loggers"""
179
180 # configure the root first
181 llist = cp.get("loggers", "keys")
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000182 llist = llist.split(",")
183 llist = list(map(lambda x: x.strip(), llist))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000184 llist.remove("root")
185 sectname = "logger_root"
186 root = logging.root
187 log = root
188 opts = cp.options(sectname)
189 if "level" in opts:
190 level = cp.get(sectname, "level")
191 log.setLevel(logging._levelNames[level])
192 for h in root.handlers[:]:
193 root.removeHandler(h)
194 hlist = cp.get(sectname, "handlers")
195 if len(hlist):
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000196 hlist = hlist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000197 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000198 for hand in hlist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +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.
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000210 existing = list(root.manager.loggerDict.keys())
Vinay Sajip95dd03b2007-11-11 14:27:30 +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.
215 existing.sort()
216 #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:
221 sectname = "logger_%s" % log
222 qn = cp.get(sectname, "qualname")
223 opts = cp.options(sectname)
224 if "propagate" in opts:
225 propagate = cp.getint(sectname, "propagate")
226 else:
227 propagate = 1
228 logger = logging.getLogger(qn)
229 if qn in existing:
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000230 i = existing.index(qn)
231 prefixed = qn + "."
232 pflen = len(prefixed)
233 num_existing = len(existing)
234 i = i + 1 # look at the entry after qn
235 while (i < num_existing) and (existing[i][:pflen] == prefixed):
236 child_loggers.append(existing[i])
237 i = i + 1
Vinay Sajip989b69a2006-01-16 21:28:37 +0000238 existing.remove(qn)
239 if "level" in opts:
240 level = cp.get(sectname, "level")
241 logger.setLevel(logging._levelNames[level])
242 for h in logger.handlers[:]:
243 logger.removeHandler(h)
244 logger.propagate = propagate
245 logger.disabled = 0
246 hlist = cp.get(sectname, "handlers")
247 if len(hlist):
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000248 hlist = hlist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000249 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000250 for hand in hlist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000251 logger.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000252
253 #Disable any old loggers. There's no point deleting
254 #them as other threads may continue to hold references
255 #and by disabling them, you stop them doing any logging.
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000256 #However, don't disable children of named loggers, as that's
257 #probably not what was intended by the user.
Vinay Sajip989b69a2006-01-16 21:28:37 +0000258 for log in existing:
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000259 logger = root.manager.loggerDict[log]
260 if log in child_loggers:
261 logger.level = logging.NOTSET
262 logger.handlers = []
263 logger.propagate = 1
Vinay Sajip5f7b97d2008-06-19 22:40:17 +0000264 elif disable_existing_loggers:
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000265 logger.disabled = 1
Vinay Sajip989b69a2006-01-16 21:28:37 +0000266
267
Vinay Sajip28c382f2010-02-04 18:48:53 +0000268
269IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
270
271
272def valid_ident(s):
273 m = IDENTIFIER.match(s)
274 if not m:
275 raise ValueError('Not a valid Python identifier: %r' % s)
276 return True
277
278
279# The ConvertingXXX classes are wrappers around standard Python containers,
280# and they serve to convert any suitable values in the container. The
281# conversion converts base dicts, lists and tuples to their wrapped
282# equivalents, whereas strings which match a conversion format are converted
283# appropriately.
284#
285# Each wrapper should have a configurator attribute holding the actual
286# configurator to use for conversion.
287
288class ConvertingDict(dict):
289 """A converting dictionary wrapper."""
290
291 def __getitem__(self, key):
292 value = dict.__getitem__(self, key)
293 result = self.configurator.convert(value)
294 #If the converted value is different, save for next time
295 if value is not result:
296 self[key] = result
297 if type(result) in (ConvertingDict, ConvertingList,
298 ConvertingTuple):
299 result.parent = self
300 result.key = key
301 return result
302
303 def get(self, key, default=None):
304 value = dict.get(self, key, default)
305 result = self.configurator.convert(value)
306 #If the converted value is different, save for next time
307 if value is not result:
308 self[key] = result
309 if type(result) in (ConvertingDict, ConvertingList,
310 ConvertingTuple):
311 result.parent = self
312 result.key = key
313 return result
314
315 def pop(self, key, default=None):
316 value = dict.pop(self, key, default)
317 result = self.configurator.convert(value)
318 if value is not result:
319 if type(result) in (ConvertingDict, ConvertingList,
320 ConvertingTuple):
321 result.parent = self
322 result.key = key
323 return result
324
325class ConvertingList(list):
326 """A converting list wrapper."""
327 def __getitem__(self, key):
328 value = list.__getitem__(self, key)
329 result = self.configurator.convert(value)
330 #If the converted value is different, save for next time
331 if value is not result:
332 self[key] = result
333 if type(result) in (ConvertingDict, ConvertingList,
334 ConvertingTuple):
335 result.parent = self
336 result.key = key
337 return result
338
339 def pop(self, idx=-1):
340 value = list.pop(self, idx)
341 result = self.configurator.convert(value)
342 if value is not result:
343 if type(result) in (ConvertingDict, ConvertingList,
344 ConvertingTuple):
345 result.parent = self
346 return result
347
348class ConvertingTuple(tuple):
349 """A converting tuple wrapper."""
350 def __getitem__(self, key):
351 value = tuple.__getitem__(self, key)
352 result = self.configurator.convert(value)
353 if value is not result:
354 if type(result) in (ConvertingDict, ConvertingList,
355 ConvertingTuple):
356 result.parent = self
357 result.key = key
358 return result
359
360class BaseConfigurator(object):
361 """
362 The configurator base class which defines some useful defaults.
363 """
364
365 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
366
367 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
368 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
369 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
370 DIGIT_PATTERN = re.compile(r'^\d+$')
371
372 value_converters = {
373 'ext' : 'ext_convert',
374 'cfg' : 'cfg_convert',
375 }
376
377 # We might want to use a different one, e.g. importlib
378 importer = __import__
379
380 def __init__(self, config):
381 self.config = ConvertingDict(config)
382 self.config.configurator = self
383
384 def resolve(self, s):
385 """
386 Resolve strings to objects using standard import and attribute
387 syntax.
388 """
389 name = s.split('.')
390 used = name.pop(0)
391 found = self.importer(used)
392 for frag in name:
393 used += '.' + frag
394 try:
395 found = getattr(found, frag)
396 except AttributeError:
397 self.importer(used)
398 found = getattr(found, frag)
399 return found
400
401 def ext_convert(self, value):
402 """Default converter for the ext:// protocol."""
403 return self.resolve(value)
404
405 def cfg_convert(self, value):
406 """Default converter for the cfg:// protocol."""
407 rest = value
408 m = self.WORD_PATTERN.match(rest)
409 if m is None:
410 raise ValueError("Unable to convert %r" % value)
411 else:
412 rest = rest[m.end():]
413 d = self.config[m.groups()[0]]
414 #print d, rest
415 while rest:
416 m = self.DOT_PATTERN.match(rest)
417 if m:
418 d = d[m.groups()[0]]
419 else:
420 m = self.INDEX_PATTERN.match(rest)
421 if m:
422 idx = m.groups()[0]
423 if not self.DIGIT_PATTERN.match(idx):
424 d = d[idx]
425 else:
426 try:
427 n = int(idx) # try as number first (most likely)
428 d = d[n]
429 except TypeError:
430 d = d[idx]
431 if m:
432 rest = rest[m.end():]
433 else:
434 raise ValueError('Unable to convert '
435 '%r at %r' % (value, rest))
436 #rest should be empty
437 return d
438
439 def convert(self, value):
440 """
441 Convert values to an appropriate type. dicts, lists and tuples are
442 replaced by their converting alternatives. Strings are checked to
443 see if they have a conversion format and are converted if they do.
444 """
445 if not isinstance(value, ConvertingDict) and isinstance(value, dict):
446 value = ConvertingDict(value)
447 value.configurator = self
448 elif not isinstance(value, ConvertingList) and isinstance(value, list):
449 value = ConvertingList(value)
450 value.configurator = self
451 elif not isinstance(value, ConvertingTuple) and\
452 isinstance(value, tuple):
453 value = ConvertingTuple(value)
454 value.configurator = self
455 elif isinstance(value, basestring): # str for py3k
456 m = self.CONVERT_PATTERN.match(value)
457 if m:
458 d = m.groupdict()
459 prefix = d['prefix']
460 converter = self.value_converters.get(prefix, None)
461 if converter:
462 suffix = d['suffix']
463 converter = getattr(self, converter)
464 value = converter(suffix)
465 return value
466
467 def configure_custom(self, config):
468 """Configure an object with a user-supplied factory."""
469 c = config.pop('()')
470 if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
471 c = self.resolve(c)
472 props = config.pop('.', None)
473 # Check for valid identifiers
474 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
475 result = c(**kwargs)
476 if props:
477 for name, value in props.items():
478 setattr(result, name, value)
479 return result
480
481class DictConfigurator(BaseConfigurator):
482 """
483 Configure logging using a dictionary-like object to describe the
484 configuration.
485 """
486
487 def configure(self):
488 """Do the configuration."""
489
490 config = self.config
491 incremental = config.pop('incremental', False)
492 EMPTY_DICT = {}
493 logging._acquireLock()
494 try:
495 if incremental:
496 handlers = config.get('handlers', EMPTY_DICT)
497 for name in handlers:
498 if name not in logging._handlers:
499 raise ValueError('No handler found with '
500 'name %r' % name)
501 else:
502 try:
503 handler = logging._handlers[name]
504 handler_config = handlers[name]
505 level = handler_config.get('level', None)
506 if level:
507 handler.setLevel(logging._checkLevel(level))
508 except StandardError, e:
509 raise ValueError('Unable to configure handler '
510 '%r: %s' % (name, e))
511 loggers = config.get('loggers', EMPTY_DICT)
512 for name in loggers:
513 try:
514 self.configure_logger(name, loggers[name], True)
515 except StandardError, e:
516 raise ValueError('Unable to configure logger '
517 '%r: %s' % (name, e))
518 root = config.get('root', None)
519 if root:
520 try:
521 self.configure_root(root, True)
522 except StandardError, e:
523 raise ValueError('Unable to configure root '
524 'logger: %s' % e)
525 else:
526 disable_existing = config.pop('disable_existing_loggers', True)
527
528 logging._handlers.clear()
529 del logging._handlerList[:]
530
531 # Do formatters first - they don't refer to anything else
532 formatters = config.get('formatters', EMPTY_DICT)
533 for name in formatters:
534 try:
535 formatters[name] = self.configure_formatter(
536 formatters[name])
537 except StandardError, e:
538 raise ValueError('Unable to configure '
539 'formatter %r: %s' % (name, e))
540 # Next, do filters - they don't refer to anything else, either
541 filters = config.get('filters', EMPTY_DICT)
542 for name in filters:
543 try:
544 filters[name] = self.configure_filter(filters[name])
545 except StandardError, e:
546 raise ValueError('Unable to configure '
547 'filter %r: %s' % (name, e))
548
549 # Next, do handlers - they refer to formatters and filters
550 # As handlers can refer to other handlers, sort the keys
551 # to allow a deterministic order of configuration
552 handlers = config.get('handlers', EMPTY_DICT)
553 for name in sorted(handlers):
554 try:
555 handler = self.configure_handler(handlers[name])
556 handler.name = name
557 handlers[name] = handler
558 except StandardError, e:
559 raise ValueError('Unable to configure handler '
560 '%r: %s' % (name, e))
561 # Next, do loggers - they refer to handlers and filters
562
563 #we don't want to lose the existing loggers,
564 #since other threads may have pointers to them.
565 #existing is set to contain all existing loggers,
566 #and as we go through the new configuration we
567 #remove any which are configured. At the end,
568 #what's left in existing is the set of loggers
569 #which were in the previous configuration but
570 #which are not in the new configuration.
571 root = logging.root
572 existing = root.manager.loggerDict.keys()
573 #The list needs to be sorted so that we can
574 #avoid disabling child loggers of explicitly
575 #named loggers. With a sorted list it is easier
576 #to find the child loggers.
577 existing.sort()
578 #We'll keep the list of existing loggers
579 #which are children of named loggers here...
580 child_loggers = []
581 #now set up the new ones...
582 loggers = config.get('loggers', EMPTY_DICT)
583 for name in loggers:
584 if name in existing:
585 i = existing.index(name)
586 prefixed = name + "."
587 pflen = len(prefixed)
588 num_existing = len(existing)
589 i = i + 1 # look at the entry after name
590 while (i < num_existing) and\
591 (existing[i][:pflen] == prefixed):
592 child_loggers.append(existing[i])
593 i = i + 1
594 existing.remove(name)
595 try:
596 self.configure_logger(name, loggers[name])
597 except StandardError, e:
598 raise ValueError('Unable to configure logger '
599 '%r: %s' % (name, e))
600
601 #Disable any old loggers. There's no point deleting
602 #them as other threads may continue to hold references
603 #and by disabling them, you stop them doing any logging.
604 #However, don't disable children of named loggers, as that's
605 #probably not what was intended by the user.
606 for log in existing:
607 logger = root.manager.loggerDict[log]
608 if log in child_loggers:
609 logger.level = logging.NOTSET
610 logger.handlers = []
611 logger.propagate = True
612 elif disable_existing:
613 logger.disabled = True
614
615 # And finally, do the root logger
616 root = config.get('root', None)
617 if root:
618 try:
619 self.configure_root(root)
620 except StandardError, e:
621 raise ValueError('Unable to configure root '
622 'logger: %s' % e)
623 finally:
624 logging._releaseLock()
625
626 def configure_formatter(self, config):
627 """Configure a formatter from a dictionary."""
628 if '()' in config:
629 factory = config['()'] # for use in exception handler
630 try:
631 result = self.configure_custom(config)
632 except TypeError, te:
633 if "'format'" not in str(te):
634 raise
635 #Name of parameter changed from fmt to format.
636 #Retry with old name.
637 #This is so that code can be used with older Python versions
638 #(e.g. by Django)
639 config['fmt'] = config.pop('format')
640 config['()'] = factory
641 result = self.configure_custom(config)
642 else:
643 fmt = config.get('format', None)
644 dfmt = config.get('datefmt', None)
645 result = logging.Formatter(fmt, dfmt)
646 return result
647
648 def configure_filter(self, config):
649 """Configure a filter from a dictionary."""
650 if '()' in config:
651 result = self.configure_custom(config)
652 else:
653 name = config.get('name', '')
654 result = logging.Filter(name)
655 return result
656
657 def add_filters(self, filterer, filters):
658 """Add filters to a filterer from a list of names."""
659 for f in filters:
660 try:
661 filterer.addFilter(self.config['filters'][f])
662 except StandardError, e:
663 raise ValueError('Unable to add filter %r: %s' % (f, e))
664
665 def configure_handler(self, config):
666 """Configure a handler from a dictionary."""
667 formatter = config.pop('formatter', None)
668 if formatter:
669 try:
670 formatter = self.config['formatters'][formatter]
671 except StandardError, e:
672 raise ValueError('Unable to set formatter '
673 '%r: %s' % (formatter, e))
674 level = config.pop('level', None)
675 filters = config.pop('filters', None)
676 if '()' in config:
677 c = config.pop('()')
678 if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
679 c = self.resolve(c)
680 factory = c
681 else:
682 klass = self.resolve(config.pop('class'))
683 #Special case for handler which refers to another handler
684 if issubclass(klass, logging.handlers.MemoryHandler) and\
685 'target' in config:
686 try:
687 config['target'] = self.config['handlers'][config['target']]
688 except StandardError, e:
689 raise ValueError('Unable to set target handler '
690 '%r: %s' % (config['target'], e))
691 factory = klass
692 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
693 try:
694 result = factory(**kwargs)
695 except TypeError, te:
696 if "'stream'" not in str(te):
697 raise
698 #The argument name changed from strm to stream
699 #Retry with old name.
700 #This is so that code can be used with older Python versions
701 #(e.g. by Django)
702 kwargs['strm'] = kwargs.pop('stream')
703 result = factory(**kwargs)
704 if formatter:
705 result.setFormatter(formatter)
706 if level is not None:
707 result.setLevel(logging._checkLevel(level))
708 if filters:
709 self.add_filters(result, filters)
710 return result
711
712 def add_handlers(self, logger, handlers):
713 """Add handlers to a logger from a list of names."""
714 for h in handlers:
715 try:
716 logger.addHandler(self.config['handlers'][h])
717 except StandardError, e:
718 raise ValueError('Unable to add handler %r: %s' % (h, e))
719
720 def common_logger_config(self, logger, config, incremental=False):
721 """
722 Perform configuration which is common to root and non-root loggers.
723 """
724 level = config.get('level', None)
725 if level is not None:
726 logger.setLevel(logging._checkLevel(level))
727 if not incremental:
728 #Remove any existing handlers
729 for h in logger.handlers[:]:
730 logger.removeHandler(h)
731 handlers = config.get('handlers', None)
732 if handlers:
733 self.add_handlers(logger, handlers)
734 filters = config.get('filters', None)
735 if filters:
736 self.add_filters(logger, filters)
737
738 def configure_logger(self, name, config, incremental=False):
739 """Configure a non-root logger from a dictionary."""
740 logger = logging.getLogger(name)
741 self.common_logger_config(logger, config, incremental)
742 propagate = config.get('propagate', None)
743 if propagate is not None:
744 logger.propagate = propagate
745
746 def configure_root(self, config, incremental=False):
747 """Configure a root logger from a dictionary."""
748 root = logging.getLogger()
749 self.common_logger_config(root, config, incremental)
750
751dictConfigClass = DictConfigurator
752
753def dictConfig(config):
754 """Configure logging using a dictionary."""
755 dictConfigClass(config).configure()
756
757
Guido van Rossum57102f82002-11-13 16:15:58 +0000758def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
759 """
760 Start up a socket server on the specified port, and listen for new
761 configurations.
762
763 These will be sent as a file suitable for processing by fileConfig().
764 Returns a Thread object on which you can call start() to start the server,
765 and which you can join() when appropriate. To stop the server, call
766 stopListening().
767 """
768 if not thread:
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000769 raise NotImplementedError("listen() needs threading to work")
Guido van Rossum57102f82002-11-13 16:15:58 +0000770
771 class ConfigStreamHandler(StreamRequestHandler):
772 """
773 Handler for a logging configuration request.
774
775 It expects a completely new logging configuration and uses fileConfig
776 to install it.
777 """
778 def handle(self):
779 """
780 Handle a request.
781
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000782 Each request is expected to be a 4-byte length, packed using
783 struct.pack(">L", n), followed by the config file.
784 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000785 """
786 import tempfile
787 try:
788 conn = self.connection
789 chunk = conn.recv(4)
790 if len(chunk) == 4:
791 slen = struct.unpack(">L", chunk)[0]
792 chunk = self.connection.recv(slen)
793 while len(chunk) < slen:
794 chunk = chunk + conn.recv(slen - len(chunk))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000795 try:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000796 import json
797 d =json.loads(chunk)
798 assert isinstance(d, dict)
799 dictConfig(d)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000800 except:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000801 #Apply new configuration.
802
803 file = cStringIO.StringIO(chunk)
804 try:
805 fileConfig(file)
806 except (KeyboardInterrupt, SystemExit):
807 raise
808 except:
809 traceback.print_exc()
Guido van Rossum57102f82002-11-13 16:15:58 +0000810 except socket.error, e:
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000811 if not isinstance(e.args, tuple):
Guido van Rossum57102f82002-11-13 16:15:58 +0000812 raise
813 else:
814 errcode = e.args[0]
815 if errcode != RESET_ERROR:
816 raise
817
818 class ConfigSocketReceiver(ThreadingTCPServer):
819 """
820 A simple TCP socket-based logging config receiver.
821 """
822
823 allow_reuse_address = 1
824
825 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000826 handler=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000827 ThreadingTCPServer.__init__(self, (host, port), handler)
828 logging._acquireLock()
829 self.abort = 0
830 logging._releaseLock()
831 self.timeout = 1
832
833 def serve_until_stopped(self):
834 import select
835 abort = 0
836 while not abort:
837 rd, wr, ex = select.select([self.socket.fileno()],
838 [], [],
839 self.timeout)
840 if rd:
841 self.handle_request()
842 logging._acquireLock()
843 abort = self.abort
844 logging._releaseLock()
845
Benjamin Peterson239f1382010-02-06 22:08:15 +0000846 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000847
Benjamin Peterson239f1382010-02-06 22:08:15 +0000848 def __init__(self, rcvr, hdlr, port):
849 super(Server, self).__init__()
850 self.rcvr = rcvr
851 self.hdlr = hdlr
852 self.port = port
853 self.ready = threading.Event()
854
855 def run(self):
856 server = self.rcvr(port=self.port, handler=self.hdlr)
857 self.ready.set()
858 global _listener
859 logging._acquireLock()
860 _listener = server
861 logging._releaseLock()
862 server.serve_until_stopped()
863
864 return Server(ConfigSocketReceiver, ConfigStreamHandler, port)
Guido van Rossum57102f82002-11-13 16:15:58 +0000865
866def stopListening():
867 """
868 Stop the listening server which was created with a call to listen().
869 """
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000870 global _listener
Guido van Rossum57102f82002-11-13 16:15:58 +0000871 if _listener:
872 logging._acquireLock()
873 _listener.abort = 1
874 _listener = None
875 logging._releaseLock()