blob: 1438388441af8e8f3cca47739e5a889007744993 [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).
Guido van Rossum57102f82002-11-13 16:15:58 +000061 """
Georg Brandl392c6fc2008-05-25 07:25:25 +000062 import ConfigParser
Guido van Rossum57102f82002-11-13 16:15:58 +000063
Georg Brandl392c6fc2008-05-25 07:25:25 +000064 cp = ConfigParser.ConfigParser(defaults)
Vinay Sajip64e8b972010-02-07 12:56:54 +000065 if hasattr(fname, 'readline'):
Guido van Rossum57102f82002-11-13 16:15:58 +000066 cp.readfp(fname)
67 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()
Georg Brandlf3e30422006-08-12 08:32:02 +000076 del logging._handlerList[:]
Vinay Sajip989b69a2006-01-16 21:28:37 +000077 # Handlers add themselves to logging._handlers
78 handlers = _install_handlers(cp, formatters)
Vinay Sajip5f7b97d2008-06-19 22:40:17 +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."""
Vinay Sajip1c77b7f2009-10-10 20:32:36 +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
Vinay Sajip6a2fd812008-09-03 09:20:05 +000098def _strip_spaces(alist):
Vinay Sajip1c77b7f2009-10-10 20:32:36 +000099 return map(lambda x: x.strip(), alist)
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000100
Vinay Sajip989b69a2006-01-16 21:28:37 +0000101def _create_formatters(cp):
102 """Create and return formatters"""
103 flist = cp.get("formatters", "keys")
104 if not len(flist):
105 return {}
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000106 flist = flist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000107 flist = _strip_spaces(flist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000108 formatters = {}
109 for form in flist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000110 sectname = "formatter_%s" % form
Vinay Sajip989b69a2006-01-16 21:28:37 +0000111 opts = cp.options(sectname)
112 if "format" in opts:
113 fs = cp.get(sectname, "format", 1)
114 else:
115 fs = None
116 if "datefmt" in opts:
117 dfs = cp.get(sectname, "datefmt", 1)
118 else:
119 dfs = None
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000120 c = logging.Formatter
121 if "class" in opts:
122 class_name = cp.get(sectname, "class")
123 if class_name:
124 c = _resolve(class_name)
125 f = c(fs, dfs)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000126 formatters[form] = f
127 return formatters
128
129
130def _install_handlers(cp, formatters):
131 """Install and return handlers"""
132 hlist = cp.get("handlers", "keys")
133 if not len(hlist):
134 return {}
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000135 hlist = hlist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000136 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000137 handlers = {}
138 fixups = [] #for inter-handler references
139 for hand in hlist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000140 sectname = "handler_%s" % hand
Vinay Sajip989b69a2006-01-16 21:28:37 +0000141 klass = cp.get(sectname, "class")
142 opts = cp.options(sectname)
143 if "formatter" in opts:
144 fmt = cp.get(sectname, "formatter")
145 else:
146 fmt = ""
Vinay Sajipbc7e34f2008-07-18 08:59:06 +0000147 try:
148 klass = eval(klass, vars(logging))
149 except (AttributeError, NameError):
150 klass = _resolve(klass)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000151 args = cp.get(sectname, "args")
152 args = eval(args, vars(logging))
Brett Cannone6bfe802008-08-04 00:09:43 +0000153 h = klass(*args)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000154 if "level" in opts:
155 level = cp.get(sectname, "level")
156 h.setLevel(logging._levelNames[level])
157 if len(fmt):
158 h.setFormatter(formatters[fmt])
Vinay Sajip5ff71712008-06-29 21:25:28 +0000159 if issubclass(klass, logging.handlers.MemoryHandler):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000160 if "target" in opts:
161 target = cp.get(sectname,"target")
162 else:
163 target = ""
164 if len(target): #the target handler may not be loaded yet, so keep for later...
165 fixups.append((h, target))
166 handlers[hand] = h
167 #now all handlers are loaded, fixup inter-handler references...
168 for h, t in fixups:
169 h.setTarget(handlers[t])
170 return handlers
171
172
Vinay Sajip5f7b97d2008-06-19 22:40:17 +0000173def _install_loggers(cp, handlers, disable_existing_loggers):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000174 """Create and install loggers"""
175
176 # configure the root first
177 llist = cp.get("loggers", "keys")
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000178 llist = llist.split(",")
179 llist = list(map(lambda x: x.strip(), llist))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000180 llist.remove("root")
181 sectname = "logger_root"
182 root = logging.root
183 log = root
184 opts = cp.options(sectname)
185 if "level" in opts:
186 level = cp.get(sectname, "level")
187 log.setLevel(logging._levelNames[level])
188 for h in root.handlers[:]:
189 root.removeHandler(h)
190 hlist = cp.get(sectname, "handlers")
191 if len(hlist):
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000192 hlist = hlist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000193 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000194 for hand in hlist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000195 log.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000196
197 #and now the others...
198 #we don't want to lose the existing loggers,
199 #since other threads may have pointers to them.
200 #existing is set to contain all existing loggers,
201 #and as we go through the new configuration we
202 #remove any which are configured. At the end,
203 #what's left in existing is the set of loggers
204 #which were in the previous configuration but
205 #which are not in the new configuration.
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000206 existing = list(root.manager.loggerDict.keys())
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000207 #The list needs to be sorted so that we can
208 #avoid disabling child loggers of explicitly
209 #named loggers. With a sorted list it is easier
210 #to find the child loggers.
211 existing.sort()
212 #We'll keep the list of existing loggers
213 #which are children of named loggers here...
214 child_loggers = []
Vinay Sajip989b69a2006-01-16 21:28:37 +0000215 #now set up the new ones...
216 for log in llist:
217 sectname = "logger_%s" % log
218 qn = cp.get(sectname, "qualname")
219 opts = cp.options(sectname)
220 if "propagate" in opts:
221 propagate = cp.getint(sectname, "propagate")
222 else:
223 propagate = 1
224 logger = logging.getLogger(qn)
225 if qn in existing:
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000226 i = existing.index(qn)
227 prefixed = qn + "."
228 pflen = len(prefixed)
229 num_existing = len(existing)
230 i = i + 1 # look at the entry after qn
231 while (i < num_existing) and (existing[i][:pflen] == prefixed):
232 child_loggers.append(existing[i])
233 i = i + 1
Vinay Sajip989b69a2006-01-16 21:28:37 +0000234 existing.remove(qn)
235 if "level" in opts:
236 level = cp.get(sectname, "level")
237 logger.setLevel(logging._levelNames[level])
238 for h in logger.handlers[:]:
239 logger.removeHandler(h)
240 logger.propagate = propagate
241 logger.disabled = 0
242 hlist = cp.get(sectname, "handlers")
243 if len(hlist):
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000244 hlist = hlist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000245 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000246 for hand in hlist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000247 logger.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000248
249 #Disable any old loggers. There's no point deleting
250 #them as other threads may continue to hold references
251 #and by disabling them, you stop them doing any logging.
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000252 #However, don't disable children of named loggers, as that's
253 #probably not what was intended by the user.
Vinay Sajip989b69a2006-01-16 21:28:37 +0000254 for log in existing:
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000255 logger = root.manager.loggerDict[log]
256 if log in child_loggers:
257 logger.level = logging.NOTSET
258 logger.handlers = []
259 logger.propagate = 1
Vinay Sajip5f7b97d2008-06-19 22:40:17 +0000260 elif disable_existing_loggers:
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000261 logger.disabled = 1
Vinay Sajip989b69a2006-01-16 21:28:37 +0000262
263
Vinay Sajip28c382f2010-02-04 18:48:53 +0000264
265IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
266
267
268def valid_ident(s):
269 m = IDENTIFIER.match(s)
270 if not m:
271 raise ValueError('Not a valid Python identifier: %r' % s)
272 return True
273
274
275# The ConvertingXXX classes are wrappers around standard Python containers,
276# and they serve to convert any suitable values in the container. The
277# conversion converts base dicts, lists and tuples to their wrapped
278# equivalents, whereas strings which match a conversion format are converted
279# appropriately.
280#
281# Each wrapper should have a configurator attribute holding the actual
282# configurator to use for conversion.
283
284class ConvertingDict(dict):
285 """A converting dictionary wrapper."""
286
287 def __getitem__(self, key):
288 value = dict.__getitem__(self, key)
289 result = self.configurator.convert(value)
290 #If the converted value is different, save for next time
291 if value is not result:
292 self[key] = result
293 if type(result) in (ConvertingDict, ConvertingList,
294 ConvertingTuple):
295 result.parent = self
296 result.key = key
297 return result
298
299 def get(self, key, default=None):
300 value = dict.get(self, key, default)
301 result = self.configurator.convert(value)
302 #If the converted value is different, save for next time
303 if value is not result:
304 self[key] = result
305 if type(result) in (ConvertingDict, ConvertingList,
306 ConvertingTuple):
307 result.parent = self
308 result.key = key
309 return result
310
311 def pop(self, key, default=None):
312 value = dict.pop(self, key, default)
313 result = self.configurator.convert(value)
314 if value is not result:
315 if type(result) in (ConvertingDict, ConvertingList,
316 ConvertingTuple):
317 result.parent = self
318 result.key = key
319 return result
320
321class ConvertingList(list):
322 """A converting list wrapper."""
323 def __getitem__(self, key):
324 value = list.__getitem__(self, key)
325 result = self.configurator.convert(value)
326 #If the converted value is different, save for next time
327 if value is not result:
328 self[key] = result
329 if type(result) in (ConvertingDict, ConvertingList,
330 ConvertingTuple):
331 result.parent = self
332 result.key = key
333 return result
334
335 def pop(self, idx=-1):
336 value = list.pop(self, idx)
337 result = self.configurator.convert(value)
338 if value is not result:
339 if type(result) in (ConvertingDict, ConvertingList,
340 ConvertingTuple):
341 result.parent = self
342 return result
343
344class ConvertingTuple(tuple):
345 """A converting tuple wrapper."""
346 def __getitem__(self, key):
347 value = tuple.__getitem__(self, key)
348 result = self.configurator.convert(value)
349 if value is not result:
350 if type(result) in (ConvertingDict, ConvertingList,
351 ConvertingTuple):
352 result.parent = self
353 result.key = key
354 return result
355
356class BaseConfigurator(object):
357 """
358 The configurator base class which defines some useful defaults.
359 """
360
361 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
362
363 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
364 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
365 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
366 DIGIT_PATTERN = re.compile(r'^\d+$')
367
368 value_converters = {
369 'ext' : 'ext_convert',
370 'cfg' : 'cfg_convert',
371 }
372
373 # We might want to use a different one, e.g. importlib
374 importer = __import__
375
376 def __init__(self, config):
377 self.config = ConvertingDict(config)
378 self.config.configurator = self
379
380 def resolve(self, s):
381 """
382 Resolve strings to objects using standard import and attribute
383 syntax.
384 """
385 name = s.split('.')
386 used = name.pop(0)
387 found = self.importer(used)
388 for frag in name:
389 used += '.' + frag
390 try:
391 found = getattr(found, frag)
392 except AttributeError:
393 self.importer(used)
394 found = getattr(found, frag)
395 return found
396
397 def ext_convert(self, value):
398 """Default converter for the ext:// protocol."""
399 return self.resolve(value)
400
401 def cfg_convert(self, value):
402 """Default converter for the cfg:// protocol."""
403 rest = value
404 m = self.WORD_PATTERN.match(rest)
405 if m is None:
406 raise ValueError("Unable to convert %r" % value)
407 else:
408 rest = rest[m.end():]
409 d = self.config[m.groups()[0]]
410 #print d, rest
411 while rest:
412 m = self.DOT_PATTERN.match(rest)
413 if m:
414 d = d[m.groups()[0]]
415 else:
416 m = self.INDEX_PATTERN.match(rest)
417 if m:
418 idx = m.groups()[0]
419 if not self.DIGIT_PATTERN.match(idx):
420 d = d[idx]
421 else:
422 try:
423 n = int(idx) # try as number first (most likely)
424 d = d[n]
425 except TypeError:
426 d = d[idx]
427 if m:
428 rest = rest[m.end():]
429 else:
430 raise ValueError('Unable to convert '
431 '%r at %r' % (value, rest))
432 #rest should be empty
433 return d
434
435 def convert(self, value):
436 """
437 Convert values to an appropriate type. dicts, lists and tuples are
438 replaced by their converting alternatives. Strings are checked to
439 see if they have a conversion format and are converted if they do.
440 """
441 if not isinstance(value, ConvertingDict) and isinstance(value, dict):
442 value = ConvertingDict(value)
443 value.configurator = self
444 elif not isinstance(value, ConvertingList) and isinstance(value, list):
445 value = ConvertingList(value)
446 value.configurator = self
447 elif not isinstance(value, ConvertingTuple) and\
448 isinstance(value, tuple):
449 value = ConvertingTuple(value)
450 value.configurator = self
451 elif isinstance(value, basestring): # str for py3k
452 m = self.CONVERT_PATTERN.match(value)
453 if m:
454 d = m.groupdict()
455 prefix = d['prefix']
456 converter = self.value_converters.get(prefix, None)
457 if converter:
458 suffix = d['suffix']
459 converter = getattr(self, converter)
460 value = converter(suffix)
461 return value
462
463 def configure_custom(self, config):
464 """Configure an object with a user-supplied factory."""
465 c = config.pop('()')
466 if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
467 c = self.resolve(c)
468 props = config.pop('.', None)
469 # Check for valid identifiers
470 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
471 result = c(**kwargs)
472 if props:
473 for name, value in props.items():
474 setattr(result, name, value)
475 return result
476
477class DictConfigurator(BaseConfigurator):
478 """
479 Configure logging using a dictionary-like object to describe the
480 configuration.
481 """
482
483 def configure(self):
484 """Do the configuration."""
485
486 config = self.config
487 incremental = config.pop('incremental', False)
488 EMPTY_DICT = {}
489 logging._acquireLock()
490 try:
491 if incremental:
492 handlers = config.get('handlers', EMPTY_DICT)
493 for name in handlers:
494 if name not in logging._handlers:
495 raise ValueError('No handler found with '
496 'name %r' % name)
497 else:
498 try:
499 handler = logging._handlers[name]
500 handler_config = handlers[name]
501 level = handler_config.get('level', None)
502 if level:
503 handler.setLevel(logging._checkLevel(level))
504 except StandardError, e:
505 raise ValueError('Unable to configure handler '
506 '%r: %s' % (name, e))
507 loggers = config.get('loggers', EMPTY_DICT)
508 for name in loggers:
509 try:
510 self.configure_logger(name, loggers[name], True)
511 except StandardError, e:
512 raise ValueError('Unable to configure logger '
513 '%r: %s' % (name, e))
514 root = config.get('root', None)
515 if root:
516 try:
517 self.configure_root(root, True)
518 except StandardError, e:
519 raise ValueError('Unable to configure root '
520 'logger: %s' % e)
521 else:
522 disable_existing = config.pop('disable_existing_loggers', True)
523
524 logging._handlers.clear()
525 del logging._handlerList[:]
526
527 # Do formatters first - they don't refer to anything else
528 formatters = config.get('formatters', EMPTY_DICT)
529 for name in formatters:
530 try:
531 formatters[name] = self.configure_formatter(
532 formatters[name])
533 except StandardError, e:
534 raise ValueError('Unable to configure '
535 'formatter %r: %s' % (name, e))
536 # Next, do filters - they don't refer to anything else, either
537 filters = config.get('filters', EMPTY_DICT)
538 for name in filters:
539 try:
540 filters[name] = self.configure_filter(filters[name])
541 except StandardError, e:
542 raise ValueError('Unable to configure '
543 'filter %r: %s' % (name, e))
544
545 # Next, do handlers - they refer to formatters and filters
546 # As handlers can refer to other handlers, sort the keys
547 # to allow a deterministic order of configuration
548 handlers = config.get('handlers', EMPTY_DICT)
549 for name in sorted(handlers):
550 try:
551 handler = self.configure_handler(handlers[name])
552 handler.name = name
553 handlers[name] = handler
554 except StandardError, e:
555 raise ValueError('Unable to configure handler '
556 '%r: %s' % (name, e))
557 # Next, do loggers - they refer to handlers and filters
558
559 #we don't want to lose the existing loggers,
560 #since other threads may have pointers to them.
561 #existing is set to contain all existing loggers,
562 #and as we go through the new configuration we
563 #remove any which are configured. At the end,
564 #what's left in existing is the set of loggers
565 #which were in the previous configuration but
566 #which are not in the new configuration.
567 root = logging.root
568 existing = root.manager.loggerDict.keys()
569 #The list needs to be sorted so that we can
570 #avoid disabling child loggers of explicitly
571 #named loggers. With a sorted list it is easier
572 #to find the child loggers.
573 existing.sort()
574 #We'll keep the list of existing loggers
575 #which are children of named loggers here...
576 child_loggers = []
577 #now set up the new ones...
578 loggers = config.get('loggers', EMPTY_DICT)
579 for name in loggers:
580 if name in existing:
581 i = existing.index(name)
582 prefixed = name + "."
583 pflen = len(prefixed)
584 num_existing = len(existing)
585 i = i + 1 # look at the entry after name
586 while (i < num_existing) and\
587 (existing[i][:pflen] == prefixed):
588 child_loggers.append(existing[i])
589 i = i + 1
590 existing.remove(name)
591 try:
592 self.configure_logger(name, loggers[name])
593 except StandardError, e:
594 raise ValueError('Unable to configure logger '
595 '%r: %s' % (name, e))
596
597 #Disable any old loggers. There's no point deleting
598 #them as other threads may continue to hold references
599 #and by disabling them, you stop them doing any logging.
600 #However, don't disable children of named loggers, as that's
601 #probably not what was intended by the user.
602 for log in existing:
603 logger = root.manager.loggerDict[log]
604 if log in child_loggers:
605 logger.level = logging.NOTSET
606 logger.handlers = []
607 logger.propagate = True
608 elif disable_existing:
609 logger.disabled = True
610
611 # And finally, do the root logger
612 root = config.get('root', None)
613 if root:
614 try:
615 self.configure_root(root)
616 except StandardError, e:
617 raise ValueError('Unable to configure root '
618 'logger: %s' % e)
619 finally:
620 logging._releaseLock()
621
622 def configure_formatter(self, config):
623 """Configure a formatter from a dictionary."""
624 if '()' in config:
625 factory = config['()'] # for use in exception handler
626 try:
627 result = self.configure_custom(config)
628 except TypeError, te:
629 if "'format'" not in str(te):
630 raise
631 #Name of parameter changed from fmt to format.
632 #Retry with old name.
633 #This is so that code can be used with older Python versions
634 #(e.g. by Django)
635 config['fmt'] = config.pop('format')
636 config['()'] = factory
637 result = self.configure_custom(config)
638 else:
639 fmt = config.get('format', None)
640 dfmt = config.get('datefmt', None)
641 result = logging.Formatter(fmt, dfmt)
642 return result
643
644 def configure_filter(self, config):
645 """Configure a filter from a dictionary."""
646 if '()' in config:
647 result = self.configure_custom(config)
648 else:
649 name = config.get('name', '')
650 result = logging.Filter(name)
651 return result
652
653 def add_filters(self, filterer, filters):
654 """Add filters to a filterer from a list of names."""
655 for f in filters:
656 try:
657 filterer.addFilter(self.config['filters'][f])
658 except StandardError, e:
659 raise ValueError('Unable to add filter %r: %s' % (f, e))
660
661 def configure_handler(self, config):
662 """Configure a handler from a dictionary."""
663 formatter = config.pop('formatter', None)
664 if formatter:
665 try:
666 formatter = self.config['formatters'][formatter]
667 except StandardError, e:
668 raise ValueError('Unable to set formatter '
669 '%r: %s' % (formatter, e))
670 level = config.pop('level', None)
671 filters = config.pop('filters', None)
672 if '()' in config:
673 c = config.pop('()')
674 if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
675 c = self.resolve(c)
676 factory = c
677 else:
678 klass = self.resolve(config.pop('class'))
679 #Special case for handler which refers to another handler
680 if issubclass(klass, logging.handlers.MemoryHandler) and\
681 'target' in config:
682 try:
683 config['target'] = self.config['handlers'][config['target']]
684 except StandardError, e:
685 raise ValueError('Unable to set target handler '
686 '%r: %s' % (config['target'], e))
687 factory = klass
688 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
689 try:
690 result = factory(**kwargs)
691 except TypeError, te:
692 if "'stream'" not in str(te):
693 raise
694 #The argument name changed from strm to stream
695 #Retry with old name.
696 #This is so that code can be used with older Python versions
697 #(e.g. by Django)
698 kwargs['strm'] = kwargs.pop('stream')
699 result = factory(**kwargs)
700 if formatter:
701 result.setFormatter(formatter)
702 if level is not None:
703 result.setLevel(logging._checkLevel(level))
704 if filters:
705 self.add_filters(result, filters)
706 return result
707
708 def add_handlers(self, logger, handlers):
709 """Add handlers to a logger from a list of names."""
710 for h in handlers:
711 try:
712 logger.addHandler(self.config['handlers'][h])
713 except StandardError, e:
714 raise ValueError('Unable to add handler %r: %s' % (h, e))
715
716 def common_logger_config(self, logger, config, incremental=False):
717 """
718 Perform configuration which is common to root and non-root loggers.
719 """
720 level = config.get('level', None)
721 if level is not None:
722 logger.setLevel(logging._checkLevel(level))
723 if not incremental:
724 #Remove any existing handlers
725 for h in logger.handlers[:]:
726 logger.removeHandler(h)
727 handlers = config.get('handlers', None)
728 if handlers:
729 self.add_handlers(logger, handlers)
730 filters = config.get('filters', None)
731 if filters:
732 self.add_filters(logger, filters)
733
734 def configure_logger(self, name, config, incremental=False):
735 """Configure a non-root logger from a dictionary."""
736 logger = logging.getLogger(name)
737 self.common_logger_config(logger, config, incremental)
738 propagate = config.get('propagate', None)
739 if propagate is not None:
740 logger.propagate = propagate
741
742 def configure_root(self, config, incremental=False):
743 """Configure a root logger from a dictionary."""
744 root = logging.getLogger()
745 self.common_logger_config(root, config, incremental)
746
747dictConfigClass = DictConfigurator
748
749def dictConfig(config):
750 """Configure logging using a dictionary."""
751 dictConfigClass(config).configure()
752
753
Guido van Rossum57102f82002-11-13 16:15:58 +0000754def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
755 """
756 Start up a socket server on the specified port, and listen for new
757 configurations.
758
759 These will be sent as a file suitable for processing by fileConfig().
760 Returns a Thread object on which you can call start() to start the server,
761 and which you can join() when appropriate. To stop the server, call
762 stopListening().
763 """
764 if not thread:
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000765 raise NotImplementedError("listen() needs threading to work")
Guido van Rossum57102f82002-11-13 16:15:58 +0000766
767 class ConfigStreamHandler(StreamRequestHandler):
768 """
769 Handler for a logging configuration request.
770
771 It expects a completely new logging configuration and uses fileConfig
772 to install it.
773 """
774 def handle(self):
775 """
776 Handle a request.
777
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000778 Each request is expected to be a 4-byte length, packed using
779 struct.pack(">L", n), followed by the config file.
780 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000781 """
782 import tempfile
783 try:
784 conn = self.connection
785 chunk = conn.recv(4)
786 if len(chunk) == 4:
787 slen = struct.unpack(">L", chunk)[0]
788 chunk = self.connection.recv(slen)
789 while len(chunk) < slen:
790 chunk = chunk + conn.recv(slen - len(chunk))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000791 try:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000792 import json
793 d =json.loads(chunk)
794 assert isinstance(d, dict)
795 dictConfig(d)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000796 except:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000797 #Apply new configuration.
798
799 file = cStringIO.StringIO(chunk)
800 try:
801 fileConfig(file)
802 except (KeyboardInterrupt, SystemExit):
803 raise
804 except:
805 traceback.print_exc()
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000806 if self.server.ready:
807 self.server.ready.set()
Guido van Rossum57102f82002-11-13 16:15:58 +0000808 except socket.error, e:
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000809 if not isinstance(e.args, tuple):
Guido van Rossum57102f82002-11-13 16:15:58 +0000810 raise
811 else:
812 errcode = e.args[0]
813 if errcode != RESET_ERROR:
814 raise
815
816 class ConfigSocketReceiver(ThreadingTCPServer):
817 """
818 A simple TCP socket-based logging config receiver.
819 """
820
821 allow_reuse_address = 1
822
823 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000824 handler=None, ready=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000825 ThreadingTCPServer.__init__(self, (host, port), handler)
826 logging._acquireLock()
827 self.abort = 0
828 logging._releaseLock()
829 self.timeout = 1
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000830 self.ready = ready
Guido van Rossum57102f82002-11-13 16:15:58 +0000831
832 def serve_until_stopped(self):
833 import select
834 abort = 0
835 while not abort:
836 rd, wr, ex = select.select([self.socket.fileno()],
837 [], [],
838 self.timeout)
839 if rd:
840 self.handle_request()
841 logging._acquireLock()
842 abort = self.abort
843 logging._releaseLock()
844
Benjamin Peterson239f1382010-02-06 22:08:15 +0000845 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000846
Benjamin Peterson239f1382010-02-06 22:08:15 +0000847 def __init__(self, rcvr, hdlr, port):
848 super(Server, self).__init__()
849 self.rcvr = rcvr
850 self.hdlr = hdlr
851 self.port = port
852 self.ready = threading.Event()
853
854 def run(self):
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000855 server = self.rcvr(port=self.port, handler=self.hdlr,
856 ready=self.ready)
Benjamin Peterson239f1382010-02-06 22:08:15 +0000857 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()