blob: e2b0163e870e10aa7fbb60ecc5ce08683409ebe3 [file] [log] [blame]
Vinay Sajip8651a512013-03-22 15:19:24 +00001# Copyright 2001-2013 by Vinay Sajip. All Rights Reserved.
Guido van Rossum57102f82002-11-13 16:15:58 +00002#
3# Permission to use, copy, modify, and distribute this software and its
4# documentation for any purpose and without fee is hereby granted,
5# provided that the above copyright notice appear in all copies and that
6# both that copyright notice and this permission notice appear in
7# supporting documentation, and that the name of Vinay Sajip
8# not be used in advertising or publicity pertaining to distribution
9# of the software without specific, written prior permission.
10# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
11# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
12# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
13# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
14# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Guido van Rossum57102f82002-11-13 16:15:58 +000016
17"""
Vinay Sajip3f742842004-02-28 16:07:46 +000018Configuration functions for the logging package for Python. The core package
19is based on PEP 282 and comments thereto in comp.lang.python, and influenced
20by Apache's log4j system.
Guido van Rossum57102f82002-11-13 16:15:58 +000021
Vinay Sajip8651a512013-03-22 15:19:24 +000022Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved.
Guido van Rossum57102f82002-11-13 16:15:58 +000023
24To use, simply 'import logging' and log away!
25"""
26
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 Sajip31e928e2010-03-22 13:02:28 +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"""
106 flist = cp.get("formatters", "keys")
107 if not len(flist):
108 return {}
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000109 flist = flist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000110 flist = _strip_spaces(flist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000111 formatters = {}
112 for form in flist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000113 sectname = "formatter_%s" % form
Vinay Sajip989b69a2006-01-16 21:28:37 +0000114 opts = cp.options(sectname)
115 if "format" in opts:
116 fs = cp.get(sectname, "format", 1)
117 else:
118 fs = None
119 if "datefmt" in opts:
120 dfs = cp.get(sectname, "datefmt", 1)
121 else:
122 dfs = None
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000123 c = logging.Formatter
124 if "class" in opts:
125 class_name = cp.get(sectname, "class")
126 if class_name:
127 c = _resolve(class_name)
128 f = c(fs, dfs)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000129 formatters[form] = f
130 return formatters
131
132
133def _install_handlers(cp, formatters):
134 """Install and return handlers"""
135 hlist = cp.get("handlers", "keys")
136 if not len(hlist):
137 return {}
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000138 hlist = hlist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000139 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000140 handlers = {}
141 fixups = [] #for inter-handler references
142 for hand in hlist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000143 sectname = "handler_%s" % hand
Vinay Sajip989b69a2006-01-16 21:28:37 +0000144 klass = cp.get(sectname, "class")
145 opts = cp.options(sectname)
146 if "formatter" in opts:
147 fmt = cp.get(sectname, "formatter")
148 else:
149 fmt = ""
Vinay Sajipbc7e34f2008-07-18 08:59:06 +0000150 try:
151 klass = eval(klass, vars(logging))
152 except (AttributeError, NameError):
153 klass = _resolve(klass)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000154 args = cp.get(sectname, "args")
155 args = eval(args, vars(logging))
Brett Cannone6bfe802008-08-04 00:09:43 +0000156 h = klass(*args)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000157 if "level" in opts:
158 level = cp.get(sectname, "level")
159 h.setLevel(logging._levelNames[level])
160 if len(fmt):
161 h.setFormatter(formatters[fmt])
Vinay Sajip5ff71712008-06-29 21:25:28 +0000162 if issubclass(klass, logging.handlers.MemoryHandler):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000163 if "target" in opts:
164 target = cp.get(sectname,"target")
165 else:
166 target = ""
167 if len(target): #the target handler may not be loaded yet, so keep for later...
168 fixups.append((h, target))
169 handlers[hand] = h
170 #now all handlers are loaded, fixup inter-handler references...
171 for h, t in fixups:
172 h.setTarget(handlers[t])
173 return handlers
174
175
Vinay Sajip5f7b97d2008-06-19 22:40:17 +0000176def _install_loggers(cp, handlers, disable_existing_loggers):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000177 """Create and install loggers"""
178
179 # configure the root first
180 llist = cp.get("loggers", "keys")
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000181 llist = llist.split(",")
182 llist = list(map(lambda x: x.strip(), llist))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000183 llist.remove("root")
184 sectname = "logger_root"
185 root = logging.root
186 log = root
187 opts = cp.options(sectname)
188 if "level" in opts:
189 level = cp.get(sectname, "level")
190 log.setLevel(logging._levelNames[level])
191 for h in root.handlers[:]:
192 root.removeHandler(h)
193 hlist = cp.get(sectname, "handlers")
194 if len(hlist):
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000195 hlist = hlist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000196 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000197 for hand in hlist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000198 log.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000199
200 #and now the others...
201 #we don't want to lose the existing loggers,
202 #since other threads may have pointers to them.
203 #existing is set to contain all existing loggers,
204 #and as we go through the new configuration we
205 #remove any which are configured. At the end,
206 #what's left in existing is the set of loggers
207 #which were in the previous configuration but
208 #which are not in the new configuration.
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000209 existing = list(root.manager.loggerDict.keys())
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000210 #The list needs to be sorted so that we can
211 #avoid disabling child loggers of explicitly
212 #named loggers. With a sorted list it is easier
213 #to find the child loggers.
Vinay Sajipcaf02722011-11-07 08:43:51 +0000214 existing.sort()
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000215 #We'll keep the list of existing loggers
216 #which are children of named loggers here...
217 child_loggers = []
Vinay Sajip989b69a2006-01-16 21:28:37 +0000218 #now set up the new ones...
219 for log in llist:
220 sectname = "logger_%s" % log
221 qn = cp.get(sectname, "qualname")
222 opts = cp.options(sectname)
223 if "propagate" in opts:
224 propagate = cp.getint(sectname, "propagate")
225 else:
226 propagate = 1
227 logger = logging.getLogger(qn)
228 if qn in existing:
Vinay Sajip8dd2a402011-03-07 15:02:11 +0000229 i = existing.index(qn) + 1 # start with the entry after qn
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000230 prefixed = qn + "."
231 pflen = len(prefixed)
232 num_existing = len(existing)
Vinay Sajip8dd2a402011-03-07 15:02:11 +0000233 while i < num_existing:
234 if existing[i][:pflen] == prefixed:
235 child_loggers.append(existing[i])
236 i += 1
Vinay Sajip989b69a2006-01-16 21:28:37 +0000237 existing.remove(qn)
238 if "level" in opts:
239 level = cp.get(sectname, "level")
240 logger.setLevel(logging._levelNames[level])
241 for h in logger.handlers[:]:
242 logger.removeHandler(h)
243 logger.propagate = propagate
244 logger.disabled = 0
245 hlist = cp.get(sectname, "handlers")
246 if len(hlist):
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000247 hlist = hlist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000248 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000249 for hand in hlist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +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.
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000255 #However, don't disable children of named loggers, as that's
256 #probably not what was intended by the user.
Vinay Sajip989b69a2006-01-16 21:28:37 +0000257 for log in existing:
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000258 logger = root.manager.loggerDict[log]
259 if log in child_loggers:
260 logger.level = logging.NOTSET
261 logger.handlers = []
262 logger.propagate = 1
Vinay Sajipbb2dad82013-03-23 11:18:10 +0000263 else:
264 logger.disabled = disable_existing_loggers
Vinay Sajip989b69a2006-01-16 21:28:37 +0000265
266
Vinay Sajip28c382f2010-02-04 18:48:53 +0000267
268IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
269
270
271def valid_ident(s):
272 m = IDENTIFIER.match(s)
273 if not m:
274 raise ValueError('Not a valid Python identifier: %r' % s)
275 return True
276
277
278# The ConvertingXXX classes are wrappers around standard Python containers,
279# and they serve to convert any suitable values in the container. The
280# conversion converts base dicts, lists and tuples to their wrapped
281# equivalents, whereas strings which match a conversion format are converted
282# appropriately.
283#
284# Each wrapper should have a configurator attribute holding the actual
285# configurator to use for conversion.
286
287class ConvertingDict(dict):
288 """A converting dictionary wrapper."""
289
290 def __getitem__(self, key):
291 value = dict.__getitem__(self, key)
292 result = self.configurator.convert(value)
293 #If the converted value is different, save for next time
294 if value is not result:
295 self[key] = result
296 if type(result) in (ConvertingDict, ConvertingList,
297 ConvertingTuple):
298 result.parent = self
299 result.key = key
300 return result
301
302 def get(self, key, default=None):
303 value = dict.get(self, key, default)
304 result = self.configurator.convert(value)
305 #If the converted value is different, save for next time
306 if value is not result:
307 self[key] = result
308 if type(result) in (ConvertingDict, ConvertingList,
309 ConvertingTuple):
310 result.parent = self
311 result.key = key
312 return result
313
314 def pop(self, key, default=None):
315 value = dict.pop(self, key, default)
316 result = self.configurator.convert(value)
317 if value is not result:
318 if type(result) in (ConvertingDict, ConvertingList,
319 ConvertingTuple):
320 result.parent = self
321 result.key = key
322 return result
323
324class ConvertingList(list):
325 """A converting list wrapper."""
326 def __getitem__(self, key):
327 value = list.__getitem__(self, key)
328 result = self.configurator.convert(value)
329 #If the converted value is different, save for next time
330 if value is not result:
331 self[key] = result
332 if type(result) in (ConvertingDict, ConvertingList,
333 ConvertingTuple):
334 result.parent = self
335 result.key = key
336 return result
337
338 def pop(self, idx=-1):
339 value = list.pop(self, idx)
340 result = self.configurator.convert(value)
341 if value is not result:
342 if type(result) in (ConvertingDict, ConvertingList,
343 ConvertingTuple):
344 result.parent = self
345 return result
346
347class ConvertingTuple(tuple):
348 """A converting tuple wrapper."""
349 def __getitem__(self, key):
350 value = tuple.__getitem__(self, key)
351 result = self.configurator.convert(value)
352 if value is not result:
353 if type(result) in (ConvertingDict, ConvertingList,
354 ConvertingTuple):
355 result.parent = self
356 result.key = key
357 return result
358
359class BaseConfigurator(object):
360 """
361 The configurator base class which defines some useful defaults.
362 """
363
364 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
365
366 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
367 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
368 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
369 DIGIT_PATTERN = re.compile(r'^\d+$')
370
371 value_converters = {
372 'ext' : 'ext_convert',
373 'cfg' : 'cfg_convert',
374 }
375
376 # We might want to use a different one, e.g. importlib
377 importer = __import__
378
379 def __init__(self, config):
380 self.config = ConvertingDict(config)
381 self.config.configurator = self
Vinay Sajip1e76e862013-03-25 23:37:41 +0000382 # Issue 12718: winpdb replaces __import__ with a Python function, which
383 # ends up being treated as a bound method. To avoid problems, we
384 # set the importer on the instance, but leave it defined in the class
385 # so existing code doesn't break
386 if type(__import__) == types.FunctionType:
387 self.importer = __import__
Vinay Sajip28c382f2010-02-04 18:48:53 +0000388
389 def resolve(self, s):
390 """
391 Resolve strings to objects using standard import and attribute
392 syntax.
393 """
394 name = s.split('.')
395 used = name.pop(0)
Vinay Sajip3b4849a2010-06-02 10:05:31 +0000396 try:
397 found = self.importer(used)
398 for frag in name:
399 used += '.' + frag
400 try:
401 found = getattr(found, frag)
402 except AttributeError:
403 self.importer(used)
404 found = getattr(found, frag)
405 return found
406 except ImportError:
407 e, tb = sys.exc_info()[1:]
408 v = ValueError('Cannot resolve %r: %s' % (s, e))
409 v.__cause__, v.__traceback__ = e, tb
410 raise v
Vinay Sajip28c382f2010-02-04 18:48:53 +0000411
412 def ext_convert(self, value):
413 """Default converter for the ext:// protocol."""
414 return self.resolve(value)
415
416 def cfg_convert(self, value):
417 """Default converter for the cfg:// protocol."""
418 rest = value
419 m = self.WORD_PATTERN.match(rest)
420 if m is None:
421 raise ValueError("Unable to convert %r" % value)
422 else:
423 rest = rest[m.end():]
424 d = self.config[m.groups()[0]]
425 #print d, rest
426 while rest:
427 m = self.DOT_PATTERN.match(rest)
428 if m:
429 d = d[m.groups()[0]]
430 else:
431 m = self.INDEX_PATTERN.match(rest)
432 if m:
433 idx = m.groups()[0]
434 if not self.DIGIT_PATTERN.match(idx):
435 d = d[idx]
436 else:
437 try:
438 n = int(idx) # try as number first (most likely)
439 d = d[n]
440 except TypeError:
441 d = d[idx]
442 if m:
443 rest = rest[m.end():]
444 else:
445 raise ValueError('Unable to convert '
446 '%r at %r' % (value, rest))
447 #rest should be empty
448 return d
449
450 def convert(self, value):
451 """
452 Convert values to an appropriate type. dicts, lists and tuples are
453 replaced by their converting alternatives. Strings are checked to
454 see if they have a conversion format and are converted if they do.
455 """
456 if not isinstance(value, ConvertingDict) and isinstance(value, dict):
457 value = ConvertingDict(value)
458 value.configurator = self
459 elif not isinstance(value, ConvertingList) and isinstance(value, list):
460 value = ConvertingList(value)
461 value.configurator = self
462 elif not isinstance(value, ConvertingTuple) and\
463 isinstance(value, tuple):
464 value = ConvertingTuple(value)
465 value.configurator = self
466 elif isinstance(value, basestring): # str for py3k
467 m = self.CONVERT_PATTERN.match(value)
468 if m:
469 d = m.groupdict()
470 prefix = d['prefix']
471 converter = self.value_converters.get(prefix, None)
472 if converter:
473 suffix = d['suffix']
474 converter = getattr(self, converter)
475 value = converter(suffix)
476 return value
477
478 def configure_custom(self, config):
479 """Configure an object with a user-supplied factory."""
480 c = config.pop('()')
481 if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
482 c = self.resolve(c)
483 props = config.pop('.', None)
484 # Check for valid identifiers
485 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
486 result = c(**kwargs)
487 if props:
488 for name, value in props.items():
489 setattr(result, name, value)
490 return result
491
Vinay Sajip1adbee22010-03-06 15:56:03 +0000492 def as_tuple(self, value):
493 """Utility function which converts lists to tuples."""
494 if isinstance(value, list):
495 value = tuple(value)
496 return value
497
Vinay Sajip28c382f2010-02-04 18:48:53 +0000498class DictConfigurator(BaseConfigurator):
499 """
500 Configure logging using a dictionary-like object to describe the
501 configuration.
502 """
503
504 def configure(self):
505 """Do the configuration."""
506
507 config = self.config
Vinay Sajipd45a2782010-03-06 15:12:08 +0000508 if 'version' not in config:
509 raise ValueError("dictionary doesn't specify a version")
510 if config['version'] != 1:
511 raise ValueError("Unsupported version: %s" % config['version'])
Vinay Sajip28c382f2010-02-04 18:48:53 +0000512 incremental = config.pop('incremental', False)
513 EMPTY_DICT = {}
514 logging._acquireLock()
515 try:
516 if incremental:
517 handlers = config.get('handlers', EMPTY_DICT)
518 for name in handlers:
519 if name not in logging._handlers:
520 raise ValueError('No handler found with '
521 'name %r' % name)
522 else:
523 try:
524 handler = logging._handlers[name]
525 handler_config = handlers[name]
526 level = handler_config.get('level', None)
527 if level:
528 handler.setLevel(logging._checkLevel(level))
529 except StandardError, e:
530 raise ValueError('Unable to configure handler '
531 '%r: %s' % (name, e))
532 loggers = config.get('loggers', EMPTY_DICT)
533 for name in loggers:
534 try:
535 self.configure_logger(name, loggers[name], True)
536 except StandardError, e:
537 raise ValueError('Unable to configure logger '
538 '%r: %s' % (name, e))
539 root = config.get('root', None)
540 if root:
541 try:
542 self.configure_root(root, True)
543 except StandardError, e:
544 raise ValueError('Unable to configure root '
545 'logger: %s' % e)
546 else:
547 disable_existing = config.pop('disable_existing_loggers', True)
548
549 logging._handlers.clear()
550 del logging._handlerList[:]
551
552 # Do formatters first - they don't refer to anything else
553 formatters = config.get('formatters', EMPTY_DICT)
554 for name in formatters:
555 try:
556 formatters[name] = self.configure_formatter(
557 formatters[name])
558 except StandardError, e:
559 raise ValueError('Unable to configure '
560 'formatter %r: %s' % (name, e))
561 # Next, do filters - they don't refer to anything else, either
562 filters = config.get('filters', EMPTY_DICT)
563 for name in filters:
564 try:
565 filters[name] = self.configure_filter(filters[name])
566 except StandardError, e:
567 raise ValueError('Unable to configure '
568 'filter %r: %s' % (name, e))
569
570 # Next, do handlers - they refer to formatters and filters
571 # As handlers can refer to other handlers, sort the keys
572 # to allow a deterministic order of configuration
573 handlers = config.get('handlers', EMPTY_DICT)
Vinay Sajip8651a512013-03-22 15:19:24 +0000574 deferred = []
Vinay Sajip28c382f2010-02-04 18:48:53 +0000575 for name in sorted(handlers):
576 try:
577 handler = self.configure_handler(handlers[name])
578 handler.name = name
579 handlers[name] = handler
580 except StandardError, e:
Vinay Sajip8651a512013-03-22 15:19:24 +0000581 if 'target not configured yet' in str(e):
582 deferred.append(name)
583 else:
584 raise ValueError('Unable to configure handler '
585 '%r: %s' % (name, e))
586
587 # Now do any that were deferred
588 for name in deferred:
589 try:
590 handler = self.configure_handler(handlers[name])
591 handler.name = name
592 handlers[name] = handler
593 except StandardError, e:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000594 raise ValueError('Unable to configure handler '
595 '%r: %s' % (name, e))
Vinay Sajip8651a512013-03-22 15:19:24 +0000596
Vinay Sajip28c382f2010-02-04 18:48:53 +0000597 # Next, do loggers - they refer to handlers and filters
598
599 #we don't want to lose the existing loggers,
600 #since other threads may have pointers to them.
601 #existing is set to contain all existing loggers,
602 #and as we go through the new configuration we
603 #remove any which are configured. At the end,
604 #what's left in existing is the set of loggers
605 #which were in the previous configuration but
606 #which are not in the new configuration.
607 root = logging.root
608 existing = root.manager.loggerDict.keys()
609 #The list needs to be sorted so that we can
610 #avoid disabling child loggers of explicitly
611 #named loggers. With a sorted list it is easier
612 #to find the child loggers.
Vinay Sajipcaf02722011-11-07 08:43:51 +0000613 existing.sort()
Vinay Sajip28c382f2010-02-04 18:48:53 +0000614 #We'll keep the list of existing loggers
615 #which are children of named loggers here...
616 child_loggers = []
617 #now set up the new ones...
618 loggers = config.get('loggers', EMPTY_DICT)
619 for name in loggers:
Vinay Sajipcaf02722011-11-07 08:43:51 +0000620 name = _encoded(name)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000621 if name in existing:
622 i = existing.index(name)
623 prefixed = name + "."
624 pflen = len(prefixed)
625 num_existing = len(existing)
626 i = i + 1 # look at the entry after name
627 while (i < num_existing) and\
628 (existing[i][:pflen] == prefixed):
629 child_loggers.append(existing[i])
630 i = i + 1
631 existing.remove(name)
632 try:
633 self.configure_logger(name, loggers[name])
634 except StandardError, e:
635 raise ValueError('Unable to configure logger '
636 '%r: %s' % (name, e))
637
638 #Disable any old loggers. There's no point deleting
639 #them as other threads may continue to hold references
640 #and by disabling them, you stop them doing any logging.
641 #However, don't disable children of named loggers, as that's
642 #probably not what was intended by the user.
643 for log in existing:
644 logger = root.manager.loggerDict[log]
645 if log in child_loggers:
646 logger.level = logging.NOTSET
647 logger.handlers = []
648 logger.propagate = True
649 elif disable_existing:
650 logger.disabled = True
651
652 # And finally, do the root logger
653 root = config.get('root', None)
654 if root:
655 try:
656 self.configure_root(root)
657 except StandardError, e:
658 raise ValueError('Unable to configure root '
659 'logger: %s' % e)
660 finally:
661 logging._releaseLock()
662
663 def configure_formatter(self, config):
664 """Configure a formatter from a dictionary."""
665 if '()' in config:
666 factory = config['()'] # for use in exception handler
667 try:
668 result = self.configure_custom(config)
669 except TypeError, te:
670 if "'format'" not in str(te):
671 raise
672 #Name of parameter changed from fmt to format.
673 #Retry with old name.
674 #This is so that code can be used with older Python versions
675 #(e.g. by Django)
676 config['fmt'] = config.pop('format')
677 config['()'] = factory
678 result = self.configure_custom(config)
679 else:
680 fmt = config.get('format', None)
681 dfmt = config.get('datefmt', None)
682 result = logging.Formatter(fmt, dfmt)
683 return result
684
685 def configure_filter(self, config):
686 """Configure a filter from a dictionary."""
687 if '()' in config:
688 result = self.configure_custom(config)
689 else:
690 name = config.get('name', '')
691 result = logging.Filter(name)
692 return result
693
694 def add_filters(self, filterer, filters):
695 """Add filters to a filterer from a list of names."""
696 for f in filters:
697 try:
698 filterer.addFilter(self.config['filters'][f])
699 except StandardError, e:
700 raise ValueError('Unable to add filter %r: %s' % (f, e))
701
702 def configure_handler(self, config):
703 """Configure a handler from a dictionary."""
704 formatter = config.pop('formatter', None)
705 if formatter:
706 try:
707 formatter = self.config['formatters'][formatter]
708 except StandardError, e:
709 raise ValueError('Unable to set formatter '
710 '%r: %s' % (formatter, e))
711 level = config.pop('level', None)
712 filters = config.pop('filters', None)
713 if '()' in config:
714 c = config.pop('()')
715 if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
716 c = self.resolve(c)
717 factory = c
718 else:
Vinay Sajip8651a512013-03-22 15:19:24 +0000719 cname = config.pop('class')
720 klass = self.resolve(cname)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000721 #Special case for handler which refers to another handler
722 if issubclass(klass, logging.handlers.MemoryHandler) and\
723 'target' in config:
724 try:
Vinay Sajip8651a512013-03-22 15:19:24 +0000725 th = self.config['handlers'][config['target']]
726 if not isinstance(th, logging.Handler):
727 config['class'] = cname # restore for deferred configuration
728 raise StandardError('target not configured yet')
729 config['target'] = th
Vinay Sajip28c382f2010-02-04 18:48:53 +0000730 except StandardError, e:
731 raise ValueError('Unable to set target handler '
732 '%r: %s' % (config['target'], e))
Vinay Sajip1adbee22010-03-06 15:56:03 +0000733 elif issubclass(klass, logging.handlers.SMTPHandler) and\
734 'mailhost' in config:
735 config['mailhost'] = self.as_tuple(config['mailhost'])
736 elif issubclass(klass, logging.handlers.SysLogHandler) and\
737 'address' in config:
738 config['address'] = self.as_tuple(config['address'])
Vinay Sajip28c382f2010-02-04 18:48:53 +0000739 factory = klass
740 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
741 try:
742 result = factory(**kwargs)
743 except TypeError, te:
744 if "'stream'" not in str(te):
745 raise
746 #The argument name changed from strm to stream
747 #Retry with old name.
748 #This is so that code can be used with older Python versions
749 #(e.g. by Django)
750 kwargs['strm'] = kwargs.pop('stream')
751 result = factory(**kwargs)
752 if formatter:
753 result.setFormatter(formatter)
754 if level is not None:
755 result.setLevel(logging._checkLevel(level))
756 if filters:
757 self.add_filters(result, filters)
758 return result
759
760 def add_handlers(self, logger, handlers):
761 """Add handlers to a logger from a list of names."""
762 for h in handlers:
763 try:
764 logger.addHandler(self.config['handlers'][h])
765 except StandardError, e:
766 raise ValueError('Unable to add handler %r: %s' % (h, e))
767
768 def common_logger_config(self, logger, config, incremental=False):
769 """
770 Perform configuration which is common to root and non-root loggers.
771 """
772 level = config.get('level', None)
773 if level is not None:
774 logger.setLevel(logging._checkLevel(level))
775 if not incremental:
776 #Remove any existing handlers
777 for h in logger.handlers[:]:
778 logger.removeHandler(h)
779 handlers = config.get('handlers', None)
780 if handlers:
781 self.add_handlers(logger, handlers)
782 filters = config.get('filters', None)
783 if filters:
784 self.add_filters(logger, filters)
785
786 def configure_logger(self, name, config, incremental=False):
787 """Configure a non-root logger from a dictionary."""
788 logger = logging.getLogger(name)
789 self.common_logger_config(logger, config, incremental)
790 propagate = config.get('propagate', None)
791 if propagate is not None:
792 logger.propagate = propagate
793
794 def configure_root(self, config, incremental=False):
795 """Configure a root logger from a dictionary."""
796 root = logging.getLogger()
797 self.common_logger_config(root, config, incremental)
798
799dictConfigClass = DictConfigurator
800
801def dictConfig(config):
802 """Configure logging using a dictionary."""
803 dictConfigClass(config).configure()
804
805
Guido van Rossum57102f82002-11-13 16:15:58 +0000806def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
807 """
808 Start up a socket server on the specified port, and listen for new
809 configurations.
810
811 These will be sent as a file suitable for processing by fileConfig().
812 Returns a Thread object on which you can call start() to start the server,
813 and which you can join() when appropriate. To stop the server, call
814 stopListening().
815 """
816 if not thread:
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000817 raise NotImplementedError("listen() needs threading to work")
Guido van Rossum57102f82002-11-13 16:15:58 +0000818
819 class ConfigStreamHandler(StreamRequestHandler):
820 """
821 Handler for a logging configuration request.
822
823 It expects a completely new logging configuration and uses fileConfig
824 to install it.
825 """
826 def handle(self):
827 """
828 Handle a request.
829
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000830 Each request is expected to be a 4-byte length, packed using
831 struct.pack(">L", n), followed by the config file.
832 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000833 """
834 import tempfile
835 try:
836 conn = self.connection
837 chunk = conn.recv(4)
838 if len(chunk) == 4:
839 slen = struct.unpack(">L", chunk)[0]
840 chunk = self.connection.recv(slen)
841 while len(chunk) < slen:
842 chunk = chunk + conn.recv(slen - len(chunk))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000843 try:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000844 import json
845 d =json.loads(chunk)
846 assert isinstance(d, dict)
847 dictConfig(d)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000848 except:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000849 #Apply new configuration.
850
851 file = cStringIO.StringIO(chunk)
852 try:
853 fileConfig(file)
854 except (KeyboardInterrupt, SystemExit):
855 raise
856 except:
857 traceback.print_exc()
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000858 if self.server.ready:
859 self.server.ready.set()
Guido van Rossum57102f82002-11-13 16:15:58 +0000860 except socket.error, e:
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000861 if not isinstance(e.args, tuple):
Guido van Rossum57102f82002-11-13 16:15:58 +0000862 raise
863 else:
864 errcode = e.args[0]
865 if errcode != RESET_ERROR:
866 raise
867
868 class ConfigSocketReceiver(ThreadingTCPServer):
869 """
870 A simple TCP socket-based logging config receiver.
871 """
872
873 allow_reuse_address = 1
874
875 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000876 handler=None, ready=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000877 ThreadingTCPServer.__init__(self, (host, port), handler)
878 logging._acquireLock()
879 self.abort = 0
880 logging._releaseLock()
881 self.timeout = 1
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000882 self.ready = ready
Guido van Rossum57102f82002-11-13 16:15:58 +0000883
884 def serve_until_stopped(self):
885 import select
886 abort = 0
887 while not abort:
888 rd, wr, ex = select.select([self.socket.fileno()],
889 [], [],
890 self.timeout)
891 if rd:
892 self.handle_request()
893 logging._acquireLock()
894 abort = self.abort
895 logging._releaseLock()
Brian Curtine27731d2010-10-31 04:44:04 +0000896 self.socket.close()
Guido van Rossum57102f82002-11-13 16:15:58 +0000897
Benjamin Peterson239f1382010-02-06 22:08:15 +0000898 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000899
Benjamin Peterson239f1382010-02-06 22:08:15 +0000900 def __init__(self, rcvr, hdlr, port):
901 super(Server, self).__init__()
902 self.rcvr = rcvr
903 self.hdlr = hdlr
904 self.port = port
905 self.ready = threading.Event()
906
907 def run(self):
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000908 server = self.rcvr(port=self.port, handler=self.hdlr,
909 ready=self.ready)
Vinay Sajip27a13702010-05-03 15:11:53 +0000910 if self.port == 0:
911 self.port = server.server_address[1]
Benjamin Peterson239f1382010-02-06 22:08:15 +0000912 self.ready.set()
913 global _listener
914 logging._acquireLock()
915 _listener = server
916 logging._releaseLock()
917 server.serve_until_stopped()
918
919 return Server(ConfigSocketReceiver, ConfigStreamHandler, port)
Guido van Rossum57102f82002-11-13 16:15:58 +0000920
921def stopListening():
922 """
923 Stop the listening server which was created with a call to listen().
924 """
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000925 global _listener
Vinay Sajip3eac5912010-09-25 17:48:25 +0000926 logging._acquireLock()
927 try:
928 if _listener:
929 _listener.abort = 1
930 _listener = None
931 finally:
Guido van Rossum57102f82002-11-13 16:15:58 +0000932 logging._releaseLock()