blob: f40c7c0aaacbaf78767c3a987d996ee125363ed7 [file] [log] [blame]
Vinay Sajip9cdf2d02014-01-30 20:22:01 +00001# Copyright 2001-2014 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 Sajip9cdf2d02014-01-30 20:22:01 +000022Copyright (C) 2001-2014 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
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000278class ConvertingMixin(object):
279 """For ConvertingXXX's, this mixin class provides common functions"""
280
281 def convert_with_key(self, key, value, replace=True):
282 result = self.configurator.convert(value)
283 #If the converted value is different, save for next time
284 if value is not result:
285 if replace:
286 self[key] = result
287 if type(result) in (ConvertingDict, ConvertingList,
288 ConvertingTuple):
289 result.parent = self
290 result.key = key
291 return result
292
293 def convert(self, value):
294 result = self.configurator.convert(value)
295 if value is not result:
296 if type(result) in (ConvertingDict, ConvertingList,
297 ConvertingTuple):
298 result.parent = self
299 return result
300
301
Vinay Sajip28c382f2010-02-04 18:48:53 +0000302# The ConvertingXXX classes are wrappers around standard Python containers,
303# and they serve to convert any suitable values in the container. The
304# conversion converts base dicts, lists and tuples to their wrapped
305# equivalents, whereas strings which match a conversion format are converted
306# appropriately.
307#
308# Each wrapper should have a configurator attribute holding the actual
309# configurator to use for conversion.
310
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000311class ConvertingDict(dict, ConvertingMixin):
Vinay Sajip28c382f2010-02-04 18:48:53 +0000312 """A converting dictionary wrapper."""
313
314 def __getitem__(self, key):
315 value = dict.__getitem__(self, key)
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000316 return self.convert_with_key(key, value)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000317
318 def get(self, key, default=None):
319 value = dict.get(self, key, default)
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000320 return self.convert_with_key(key, value)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000321
322 def pop(self, key, default=None):
323 value = dict.pop(self, key, default)
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000324 return self.convert_with_key(key, value, replace=False)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000325
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000326class ConvertingList(list, ConvertingMixin):
Vinay Sajip28c382f2010-02-04 18:48:53 +0000327 """A converting list wrapper."""
328 def __getitem__(self, key):
329 value = list.__getitem__(self, key)
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000330 return self.convert_with_key(key, value)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000331
332 def pop(self, idx=-1):
333 value = list.pop(self, idx)
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000334 return self.convert(value)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000335
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000336class ConvertingTuple(tuple, ConvertingMixin):
Vinay Sajip28c382f2010-02-04 18:48:53 +0000337 """A converting tuple wrapper."""
338 def __getitem__(self, key):
339 value = tuple.__getitem__(self, key)
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000340 # Can't replace a tuple entry.
341 return self.convert_with_key(key, value, replace=False)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000342
343class BaseConfigurator(object):
344 """
345 The configurator base class which defines some useful defaults.
346 """
347
348 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
349
350 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
351 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
352 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
353 DIGIT_PATTERN = re.compile(r'^\d+$')
354
355 value_converters = {
356 'ext' : 'ext_convert',
357 'cfg' : 'cfg_convert',
358 }
359
360 # We might want to use a different one, e.g. importlib
361 importer = __import__
362
363 def __init__(self, config):
364 self.config = ConvertingDict(config)
365 self.config.configurator = self
Vinay Sajip1e76e862013-03-25 23:37:41 +0000366 # Issue 12718: winpdb replaces __import__ with a Python function, which
367 # ends up being treated as a bound method. To avoid problems, we
368 # set the importer on the instance, but leave it defined in the class
369 # so existing code doesn't break
370 if type(__import__) == types.FunctionType:
371 self.importer = __import__
Vinay Sajip28c382f2010-02-04 18:48:53 +0000372
373 def resolve(self, s):
374 """
375 Resolve strings to objects using standard import and attribute
376 syntax.
377 """
378 name = s.split('.')
379 used = name.pop(0)
Vinay Sajip3b4849a2010-06-02 10:05:31 +0000380 try:
381 found = self.importer(used)
382 for frag in name:
383 used += '.' + frag
384 try:
385 found = getattr(found, frag)
386 except AttributeError:
387 self.importer(used)
388 found = getattr(found, frag)
389 return found
390 except ImportError:
391 e, tb = sys.exc_info()[1:]
392 v = ValueError('Cannot resolve %r: %s' % (s, e))
393 v.__cause__, v.__traceback__ = e, tb
394 raise v
Vinay Sajip28c382f2010-02-04 18:48:53 +0000395
396 def ext_convert(self, value):
397 """Default converter for the ext:// protocol."""
398 return self.resolve(value)
399
400 def cfg_convert(self, value):
401 """Default converter for the cfg:// protocol."""
402 rest = value
403 m = self.WORD_PATTERN.match(rest)
404 if m is None:
405 raise ValueError("Unable to convert %r" % value)
406 else:
407 rest = rest[m.end():]
408 d = self.config[m.groups()[0]]
409 #print d, rest
410 while rest:
411 m = self.DOT_PATTERN.match(rest)
412 if m:
413 d = d[m.groups()[0]]
414 else:
415 m = self.INDEX_PATTERN.match(rest)
416 if m:
417 idx = m.groups()[0]
418 if not self.DIGIT_PATTERN.match(idx):
419 d = d[idx]
420 else:
421 try:
422 n = int(idx) # try as number first (most likely)
423 d = d[n]
424 except TypeError:
425 d = d[idx]
426 if m:
427 rest = rest[m.end():]
428 else:
429 raise ValueError('Unable to convert '
430 '%r at %r' % (value, rest))
431 #rest should be empty
432 return d
433
434 def convert(self, value):
435 """
436 Convert values to an appropriate type. dicts, lists and tuples are
437 replaced by their converting alternatives. Strings are checked to
438 see if they have a conversion format and are converted if they do.
439 """
440 if not isinstance(value, ConvertingDict) and isinstance(value, dict):
441 value = ConvertingDict(value)
442 value.configurator = self
443 elif not isinstance(value, ConvertingList) and isinstance(value, list):
444 value = ConvertingList(value)
445 value.configurator = self
446 elif not isinstance(value, ConvertingTuple) and\
447 isinstance(value, tuple):
448 value = ConvertingTuple(value)
449 value.configurator = self
450 elif isinstance(value, basestring): # str for py3k
451 m = self.CONVERT_PATTERN.match(value)
452 if m:
453 d = m.groupdict()
454 prefix = d['prefix']
455 converter = self.value_converters.get(prefix, None)
456 if converter:
457 suffix = d['suffix']
458 converter = getattr(self, converter)
459 value = converter(suffix)
460 return value
461
462 def configure_custom(self, config):
463 """Configure an object with a user-supplied factory."""
464 c = config.pop('()')
465 if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
466 c = self.resolve(c)
467 props = config.pop('.', None)
468 # Check for valid identifiers
469 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
470 result = c(**kwargs)
471 if props:
472 for name, value in props.items():
473 setattr(result, name, value)
474 return result
475
Vinay Sajip1adbee22010-03-06 15:56:03 +0000476 def as_tuple(self, value):
477 """Utility function which converts lists to tuples."""
478 if isinstance(value, list):
479 value = tuple(value)
480 return value
481
Vinay Sajip28c382f2010-02-04 18:48:53 +0000482class DictConfigurator(BaseConfigurator):
483 """
484 Configure logging using a dictionary-like object to describe the
485 configuration.
486 """
487
488 def configure(self):
489 """Do the configuration."""
490
491 config = self.config
Vinay Sajipd45a2782010-03-06 15:12:08 +0000492 if 'version' not in config:
493 raise ValueError("dictionary doesn't specify a version")
494 if config['version'] != 1:
495 raise ValueError("Unsupported version: %s" % config['version'])
Vinay Sajip28c382f2010-02-04 18:48:53 +0000496 incremental = config.pop('incremental', False)
497 EMPTY_DICT = {}
498 logging._acquireLock()
499 try:
500 if incremental:
501 handlers = config.get('handlers', EMPTY_DICT)
502 for name in handlers:
503 if name not in logging._handlers:
504 raise ValueError('No handler found with '
505 'name %r' % name)
506 else:
507 try:
508 handler = logging._handlers[name]
509 handler_config = handlers[name]
510 level = handler_config.get('level', None)
511 if level:
512 handler.setLevel(logging._checkLevel(level))
513 except StandardError, e:
514 raise ValueError('Unable to configure handler '
515 '%r: %s' % (name, e))
516 loggers = config.get('loggers', EMPTY_DICT)
517 for name in loggers:
518 try:
519 self.configure_logger(name, loggers[name], True)
520 except StandardError, e:
521 raise ValueError('Unable to configure logger '
522 '%r: %s' % (name, e))
523 root = config.get('root', None)
524 if root:
525 try:
526 self.configure_root(root, True)
527 except StandardError, e:
528 raise ValueError('Unable to configure root '
529 'logger: %s' % e)
530 else:
531 disable_existing = config.pop('disable_existing_loggers', True)
532
533 logging._handlers.clear()
534 del logging._handlerList[:]
535
536 # Do formatters first - they don't refer to anything else
537 formatters = config.get('formatters', EMPTY_DICT)
538 for name in formatters:
539 try:
540 formatters[name] = self.configure_formatter(
541 formatters[name])
542 except StandardError, e:
543 raise ValueError('Unable to configure '
544 'formatter %r: %s' % (name, e))
545 # Next, do filters - they don't refer to anything else, either
546 filters = config.get('filters', EMPTY_DICT)
547 for name in filters:
548 try:
549 filters[name] = self.configure_filter(filters[name])
550 except StandardError, e:
551 raise ValueError('Unable to configure '
552 'filter %r: %s' % (name, e))
553
554 # Next, do handlers - they refer to formatters and filters
555 # As handlers can refer to other handlers, sort the keys
556 # to allow a deterministic order of configuration
557 handlers = config.get('handlers', EMPTY_DICT)
Vinay Sajip8651a512013-03-22 15:19:24 +0000558 deferred = []
Vinay Sajip28c382f2010-02-04 18:48:53 +0000559 for name in sorted(handlers):
560 try:
561 handler = self.configure_handler(handlers[name])
562 handler.name = name
563 handlers[name] = handler
564 except StandardError, e:
Vinay Sajip8651a512013-03-22 15:19:24 +0000565 if 'target not configured yet' in str(e):
566 deferred.append(name)
567 else:
568 raise ValueError('Unable to configure handler '
569 '%r: %s' % (name, e))
570
571 # Now do any that were deferred
572 for name in deferred:
573 try:
574 handler = self.configure_handler(handlers[name])
575 handler.name = name
576 handlers[name] = handler
577 except StandardError, e:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000578 raise ValueError('Unable to configure handler '
579 '%r: %s' % (name, e))
Vinay Sajip8651a512013-03-22 15:19:24 +0000580
Vinay Sajip28c382f2010-02-04 18:48:53 +0000581 # Next, do loggers - they refer to handlers and filters
582
583 #we don't want to lose the existing loggers,
584 #since other threads may have pointers to them.
585 #existing is set to contain all existing loggers,
586 #and as we go through the new configuration we
587 #remove any which are configured. At the end,
588 #what's left in existing is the set of loggers
589 #which were in the previous configuration but
590 #which are not in the new configuration.
591 root = logging.root
592 existing = root.manager.loggerDict.keys()
593 #The list needs to be sorted so that we can
594 #avoid disabling child loggers of explicitly
595 #named loggers. With a sorted list it is easier
596 #to find the child loggers.
Vinay Sajipcaf02722011-11-07 08:43:51 +0000597 existing.sort()
Vinay Sajip28c382f2010-02-04 18:48:53 +0000598 #We'll keep the list of existing loggers
599 #which are children of named loggers here...
600 child_loggers = []
601 #now set up the new ones...
602 loggers = config.get('loggers', EMPTY_DICT)
603 for name in loggers:
Vinay Sajipcaf02722011-11-07 08:43:51 +0000604 name = _encoded(name)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000605 if name in existing:
606 i = existing.index(name)
607 prefixed = name + "."
608 pflen = len(prefixed)
609 num_existing = len(existing)
610 i = i + 1 # look at the entry after name
611 while (i < num_existing) and\
612 (existing[i][:pflen] == prefixed):
613 child_loggers.append(existing[i])
614 i = i + 1
615 existing.remove(name)
616 try:
617 self.configure_logger(name, loggers[name])
618 except StandardError, e:
619 raise ValueError('Unable to configure logger '
620 '%r: %s' % (name, e))
621
622 #Disable any old loggers. There's no point deleting
623 #them as other threads may continue to hold references
624 #and by disabling them, you stop them doing any logging.
625 #However, don't disable children of named loggers, as that's
626 #probably not what was intended by the user.
627 for log in existing:
628 logger = root.manager.loggerDict[log]
629 if log in child_loggers:
630 logger.level = logging.NOTSET
631 logger.handlers = []
632 logger.propagate = True
633 elif disable_existing:
634 logger.disabled = True
635
636 # And finally, do the root logger
637 root = config.get('root', None)
638 if root:
639 try:
640 self.configure_root(root)
641 except StandardError, e:
642 raise ValueError('Unable to configure root '
643 'logger: %s' % e)
644 finally:
645 logging._releaseLock()
646
647 def configure_formatter(self, config):
648 """Configure a formatter from a dictionary."""
649 if '()' in config:
650 factory = config['()'] # for use in exception handler
651 try:
652 result = self.configure_custom(config)
653 except TypeError, te:
654 if "'format'" not in str(te):
655 raise
656 #Name of parameter changed from fmt to format.
657 #Retry with old name.
658 #This is so that code can be used with older Python versions
659 #(e.g. by Django)
660 config['fmt'] = config.pop('format')
661 config['()'] = factory
662 result = self.configure_custom(config)
663 else:
664 fmt = config.get('format', None)
665 dfmt = config.get('datefmt', None)
666 result = logging.Formatter(fmt, dfmt)
667 return result
668
669 def configure_filter(self, config):
670 """Configure a filter from a dictionary."""
671 if '()' in config:
672 result = self.configure_custom(config)
673 else:
674 name = config.get('name', '')
675 result = logging.Filter(name)
676 return result
677
678 def add_filters(self, filterer, filters):
679 """Add filters to a filterer from a list of names."""
680 for f in filters:
681 try:
682 filterer.addFilter(self.config['filters'][f])
683 except StandardError, e:
684 raise ValueError('Unable to add filter %r: %s' % (f, e))
685
686 def configure_handler(self, config):
687 """Configure a handler from a dictionary."""
688 formatter = config.pop('formatter', None)
689 if formatter:
690 try:
691 formatter = self.config['formatters'][formatter]
692 except StandardError, e:
693 raise ValueError('Unable to set formatter '
694 '%r: %s' % (formatter, e))
695 level = config.pop('level', None)
696 filters = config.pop('filters', None)
697 if '()' in config:
698 c = config.pop('()')
699 if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
700 c = self.resolve(c)
701 factory = c
702 else:
Vinay Sajip8651a512013-03-22 15:19:24 +0000703 cname = config.pop('class')
704 klass = self.resolve(cname)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000705 #Special case for handler which refers to another handler
706 if issubclass(klass, logging.handlers.MemoryHandler) and\
707 'target' in config:
708 try:
Vinay Sajip8651a512013-03-22 15:19:24 +0000709 th = self.config['handlers'][config['target']]
710 if not isinstance(th, logging.Handler):
711 config['class'] = cname # restore for deferred configuration
712 raise StandardError('target not configured yet')
713 config['target'] = th
Vinay Sajip28c382f2010-02-04 18:48:53 +0000714 except StandardError, e:
715 raise ValueError('Unable to set target handler '
716 '%r: %s' % (config['target'], e))
Vinay Sajip1adbee22010-03-06 15:56:03 +0000717 elif issubclass(klass, logging.handlers.SMTPHandler) and\
718 'mailhost' in config:
719 config['mailhost'] = self.as_tuple(config['mailhost'])
720 elif issubclass(klass, logging.handlers.SysLogHandler) and\
721 'address' in config:
722 config['address'] = self.as_tuple(config['address'])
Vinay Sajip28c382f2010-02-04 18:48:53 +0000723 factory = klass
724 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
725 try:
726 result = factory(**kwargs)
727 except TypeError, te:
728 if "'stream'" not in str(te):
729 raise
730 #The argument name changed from strm to stream
731 #Retry with old name.
732 #This is so that code can be used with older Python versions
733 #(e.g. by Django)
734 kwargs['strm'] = kwargs.pop('stream')
735 result = factory(**kwargs)
736 if formatter:
737 result.setFormatter(formatter)
738 if level is not None:
739 result.setLevel(logging._checkLevel(level))
740 if filters:
741 self.add_filters(result, filters)
742 return result
743
744 def add_handlers(self, logger, handlers):
745 """Add handlers to a logger from a list of names."""
746 for h in handlers:
747 try:
748 logger.addHandler(self.config['handlers'][h])
749 except StandardError, e:
750 raise ValueError('Unable to add handler %r: %s' % (h, e))
751
752 def common_logger_config(self, logger, config, incremental=False):
753 """
754 Perform configuration which is common to root and non-root loggers.
755 """
756 level = config.get('level', None)
757 if level is not None:
758 logger.setLevel(logging._checkLevel(level))
759 if not incremental:
760 #Remove any existing handlers
761 for h in logger.handlers[:]:
762 logger.removeHandler(h)
763 handlers = config.get('handlers', None)
764 if handlers:
765 self.add_handlers(logger, handlers)
766 filters = config.get('filters', None)
767 if filters:
768 self.add_filters(logger, filters)
769
770 def configure_logger(self, name, config, incremental=False):
771 """Configure a non-root logger from a dictionary."""
772 logger = logging.getLogger(name)
773 self.common_logger_config(logger, config, incremental)
774 propagate = config.get('propagate', None)
775 if propagate is not None:
776 logger.propagate = propagate
777
778 def configure_root(self, config, incremental=False):
779 """Configure a root logger from a dictionary."""
780 root = logging.getLogger()
781 self.common_logger_config(root, config, incremental)
782
783dictConfigClass = DictConfigurator
784
785def dictConfig(config):
786 """Configure logging using a dictionary."""
787 dictConfigClass(config).configure()
788
789
Guido van Rossum57102f82002-11-13 16:15:58 +0000790def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
791 """
792 Start up a socket server on the specified port, and listen for new
793 configurations.
794
795 These will be sent as a file suitable for processing by fileConfig().
796 Returns a Thread object on which you can call start() to start the server,
797 and which you can join() when appropriate. To stop the server, call
798 stopListening().
799 """
800 if not thread:
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000801 raise NotImplementedError("listen() needs threading to work")
Guido van Rossum57102f82002-11-13 16:15:58 +0000802
803 class ConfigStreamHandler(StreamRequestHandler):
804 """
805 Handler for a logging configuration request.
806
807 It expects a completely new logging configuration and uses fileConfig
808 to install it.
809 """
810 def handle(self):
811 """
812 Handle a request.
813
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000814 Each request is expected to be a 4-byte length, packed using
815 struct.pack(">L", n), followed by the config file.
816 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000817 """
818 import tempfile
819 try:
820 conn = self.connection
821 chunk = conn.recv(4)
822 if len(chunk) == 4:
823 slen = struct.unpack(">L", chunk)[0]
824 chunk = self.connection.recv(slen)
825 while len(chunk) < slen:
826 chunk = chunk + conn.recv(slen - len(chunk))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000827 try:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000828 import json
829 d =json.loads(chunk)
830 assert isinstance(d, dict)
831 dictConfig(d)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000832 except:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000833 #Apply new configuration.
834
835 file = cStringIO.StringIO(chunk)
836 try:
837 fileConfig(file)
838 except (KeyboardInterrupt, SystemExit):
839 raise
840 except:
841 traceback.print_exc()
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000842 if self.server.ready:
843 self.server.ready.set()
Guido van Rossum57102f82002-11-13 16:15:58 +0000844 except socket.error, e:
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000845 if not isinstance(e.args, tuple):
Guido van Rossum57102f82002-11-13 16:15:58 +0000846 raise
847 else:
848 errcode = e.args[0]
849 if errcode != RESET_ERROR:
850 raise
851
852 class ConfigSocketReceiver(ThreadingTCPServer):
853 """
854 A simple TCP socket-based logging config receiver.
855 """
856
857 allow_reuse_address = 1
858
859 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000860 handler=None, ready=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000861 ThreadingTCPServer.__init__(self, (host, port), handler)
862 logging._acquireLock()
863 self.abort = 0
864 logging._releaseLock()
865 self.timeout = 1
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000866 self.ready = ready
Guido van Rossum57102f82002-11-13 16:15:58 +0000867
868 def serve_until_stopped(self):
869 import select
870 abort = 0
871 while not abort:
872 rd, wr, ex = select.select([self.socket.fileno()],
873 [], [],
874 self.timeout)
875 if rd:
876 self.handle_request()
877 logging._acquireLock()
878 abort = self.abort
879 logging._releaseLock()
Brian Curtine27731d2010-10-31 04:44:04 +0000880 self.socket.close()
Guido van Rossum57102f82002-11-13 16:15:58 +0000881
Benjamin Peterson239f1382010-02-06 22:08:15 +0000882 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000883
Benjamin Peterson239f1382010-02-06 22:08:15 +0000884 def __init__(self, rcvr, hdlr, port):
885 super(Server, self).__init__()
886 self.rcvr = rcvr
887 self.hdlr = hdlr
888 self.port = port
889 self.ready = threading.Event()
890
891 def run(self):
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000892 server = self.rcvr(port=self.port, handler=self.hdlr,
893 ready=self.ready)
Vinay Sajip27a13702010-05-03 15:11:53 +0000894 if self.port == 0:
895 self.port = server.server_address[1]
Benjamin Peterson239f1382010-02-06 22:08:15 +0000896 self.ready.set()
897 global _listener
898 logging._acquireLock()
899 _listener = server
900 logging._releaseLock()
901 server.serve_until_stopped()
902
903 return Server(ConfigSocketReceiver, ConfigStreamHandler, port)
Guido van Rossum57102f82002-11-13 16:15:58 +0000904
905def stopListening():
906 """
907 Stop the listening server which was created with a call to listen().
908 """
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000909 global _listener
Vinay Sajip3eac5912010-09-25 17:48:25 +0000910 logging._acquireLock()
911 try:
912 if _listener:
913 _listener.abort = 1
914 _listener = None
915 finally:
Guido van Rossum57102f82002-11-13 16:15:58 +0000916 logging._releaseLock()