blob: 8b3795675dc13ccb70917906121a61a4298571a4 [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 Sajip79ce12e2014-02-11 11:32:40 +000027import cStringIO
28import errno
29import io
30import logging
31import logging.handlers
32import os
33import re
34import socket
35import struct
36import sys
37import traceback
38import types
Vinay Sajip612df8e2005-02-18 11:54:46 +000039
40try:
41 import thread
42 import threading
43except ImportError:
44 thread = None
Guido van Rossum57102f82002-11-13 16:15:58 +000045
Georg Brandle152a772008-05-24 18:31:28 +000046from SocketServer import ThreadingTCPServer, StreamRequestHandler
Guido van Rossum57102f82002-11-13 16:15:58 +000047
48
49DEFAULT_LOGGING_CONFIG_PORT = 9030
50
Vinay Sajip79ce12e2014-02-11 11:32:40 +000051RESET_ERROR = errno.ECONNRESET
Vinay Sajip326441e2004-02-20 13:16:36 +000052
Guido van Rossum57102f82002-11-13 16:15:58 +000053#
54# The following code implements a socket listener for on-the-fly
55# reconfiguration of logging.
56#
57# _listener holds the server object doing the listening
58_listener = None
59
Vinay Sajip1c77b7f2009-10-10 20:32:36 +000060def fileConfig(fname, defaults=None, disable_existing_loggers=True):
Guido van Rossum57102f82002-11-13 16:15:58 +000061 """
62 Read the logging configuration from a ConfigParser-format file.
63
64 This can be called several times from an application, allowing an end user
65 the ability to select from various pre-canned configurations (if the
66 developer provides a mechanism to present the choices and load the chosen
67 configuration).
Guido van Rossum57102f82002-11-13 16:15:58 +000068 """
Georg Brandl392c6fc2008-05-25 07:25:25 +000069 import ConfigParser
Guido van Rossum57102f82002-11-13 16:15:58 +000070
Georg Brandl392c6fc2008-05-25 07:25:25 +000071 cp = ConfigParser.ConfigParser(defaults)
Vinay Sajip64e8b972010-02-07 12:56:54 +000072 if hasattr(fname, 'readline'):
Guido van Rossum57102f82002-11-13 16:15:58 +000073 cp.readfp(fname)
74 else:
75 cp.read(fname)
Vinay Sajip989b69a2006-01-16 21:28:37 +000076
77 formatters = _create_formatters(cp)
78
79 # critical section
Guido van Rossum57102f82002-11-13 16:15:58 +000080 logging._acquireLock()
81 try:
Vinay Sajip989b69a2006-01-16 21:28:37 +000082 logging._handlers.clear()
Georg Brandlf3e30422006-08-12 08:32:02 +000083 del logging._handlerList[:]
Vinay Sajip989b69a2006-01-16 21:28:37 +000084 # Handlers add themselves to logging._handlers
85 handlers = _install_handlers(cp, formatters)
Vinay Sajip5f7b97d2008-06-19 22:40:17 +000086 _install_loggers(cp, handlers, disable_existing_loggers)
Guido van Rossum57102f82002-11-13 16:15:58 +000087 finally:
88 logging._releaseLock()
89
Vinay Sajip989b69a2006-01-16 21:28:37 +000090
Vinay Sajip7a7160b2006-01-20 18:28:03 +000091def _resolve(name):
92 """Resolve a dotted name to a global object."""
Vinay Sajip1c77b7f2009-10-10 20:32:36 +000093 name = name.split('.')
Vinay Sajip7a7160b2006-01-20 18:28:03 +000094 used = name.pop(0)
95 found = __import__(used)
96 for n in name:
97 used = used + '.' + n
98 try:
99 found = getattr(found, n)
100 except AttributeError:
101 __import__(used)
102 found = getattr(found, n)
103 return found
104
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000105def _strip_spaces(alist):
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000106 return map(lambda x: x.strip(), alist)
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000107
Vinay Sajip31e928e2010-03-22 13:02:28 +0000108def _encoded(s):
109 return s if isinstance(s, str) else s.encode('utf-8')
110
Vinay Sajip989b69a2006-01-16 21:28:37 +0000111def _create_formatters(cp):
112 """Create and return formatters"""
113 flist = cp.get("formatters", "keys")
114 if not len(flist):
115 return {}
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000116 flist = flist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000117 flist = _strip_spaces(flist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000118 formatters = {}
119 for form in flist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000120 sectname = "formatter_%s" % form
Vinay Sajip989b69a2006-01-16 21:28:37 +0000121 opts = cp.options(sectname)
122 if "format" in opts:
123 fs = cp.get(sectname, "format", 1)
124 else:
125 fs = None
126 if "datefmt" in opts:
127 dfs = cp.get(sectname, "datefmt", 1)
128 else:
129 dfs = None
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000130 c = logging.Formatter
131 if "class" in opts:
132 class_name = cp.get(sectname, "class")
133 if class_name:
134 c = _resolve(class_name)
135 f = c(fs, dfs)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000136 formatters[form] = f
137 return formatters
138
139
140def _install_handlers(cp, formatters):
141 """Install and return handlers"""
142 hlist = cp.get("handlers", "keys")
143 if not len(hlist):
144 return {}
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000145 hlist = hlist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000146 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000147 handlers = {}
148 fixups = [] #for inter-handler references
149 for hand in hlist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000150 sectname = "handler_%s" % hand
Vinay Sajip989b69a2006-01-16 21:28:37 +0000151 klass = cp.get(sectname, "class")
152 opts = cp.options(sectname)
153 if "formatter" in opts:
154 fmt = cp.get(sectname, "formatter")
155 else:
156 fmt = ""
Vinay Sajipbc7e34f2008-07-18 08:59:06 +0000157 try:
158 klass = eval(klass, vars(logging))
159 except (AttributeError, NameError):
160 klass = _resolve(klass)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000161 args = cp.get(sectname, "args")
162 args = eval(args, vars(logging))
Brett Cannone6bfe802008-08-04 00:09:43 +0000163 h = klass(*args)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000164 if "level" in opts:
165 level = cp.get(sectname, "level")
166 h.setLevel(logging._levelNames[level])
167 if len(fmt):
168 h.setFormatter(formatters[fmt])
Vinay Sajip5ff71712008-06-29 21:25:28 +0000169 if issubclass(klass, logging.handlers.MemoryHandler):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000170 if "target" in opts:
171 target = cp.get(sectname,"target")
172 else:
173 target = ""
174 if len(target): #the target handler may not be loaded yet, so keep for later...
175 fixups.append((h, target))
176 handlers[hand] = h
177 #now all handlers are loaded, fixup inter-handler references...
178 for h, t in fixups:
179 h.setTarget(handlers[t])
180 return handlers
181
182
Vinay Sajip5f7b97d2008-06-19 22:40:17 +0000183def _install_loggers(cp, handlers, disable_existing_loggers):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000184 """Create and install loggers"""
185
186 # configure the root first
187 llist = cp.get("loggers", "keys")
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000188 llist = llist.split(",")
189 llist = list(map(lambda x: x.strip(), llist))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000190 llist.remove("root")
191 sectname = "logger_root"
192 root = logging.root
193 log = root
194 opts = cp.options(sectname)
195 if "level" in opts:
196 level = cp.get(sectname, "level")
197 log.setLevel(logging._levelNames[level])
198 for h in root.handlers[:]:
199 root.removeHandler(h)
200 hlist = cp.get(sectname, "handlers")
201 if len(hlist):
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000202 hlist = hlist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000203 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000204 for hand in hlist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000205 log.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000206
207 #and now the others...
208 #we don't want to lose the existing loggers,
209 #since other threads may have pointers to them.
210 #existing is set to contain all existing loggers,
211 #and as we go through the new configuration we
212 #remove any which are configured. At the end,
213 #what's left in existing is the set of loggers
214 #which were in the previous configuration but
215 #which are not in the new configuration.
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000216 existing = list(root.manager.loggerDict.keys())
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000217 #The list needs to be sorted so that we can
218 #avoid disabling child loggers of explicitly
219 #named loggers. With a sorted list it is easier
220 #to find the child loggers.
Vinay Sajipcaf02722011-11-07 08:43:51 +0000221 existing.sort()
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000222 #We'll keep the list of existing loggers
223 #which are children of named loggers here...
224 child_loggers = []
Vinay Sajip989b69a2006-01-16 21:28:37 +0000225 #now set up the new ones...
226 for log in llist:
227 sectname = "logger_%s" % log
228 qn = cp.get(sectname, "qualname")
229 opts = cp.options(sectname)
230 if "propagate" in opts:
231 propagate = cp.getint(sectname, "propagate")
232 else:
233 propagate = 1
234 logger = logging.getLogger(qn)
235 if qn in existing:
Vinay Sajip8dd2a402011-03-07 15:02:11 +0000236 i = existing.index(qn) + 1 # start with the entry after qn
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000237 prefixed = qn + "."
238 pflen = len(prefixed)
239 num_existing = len(existing)
Vinay Sajip8dd2a402011-03-07 15:02:11 +0000240 while i < num_existing:
241 if existing[i][:pflen] == prefixed:
242 child_loggers.append(existing[i])
243 i += 1
Vinay Sajip989b69a2006-01-16 21:28:37 +0000244 existing.remove(qn)
245 if "level" in opts:
246 level = cp.get(sectname, "level")
247 logger.setLevel(logging._levelNames[level])
248 for h in logger.handlers[:]:
249 logger.removeHandler(h)
250 logger.propagate = propagate
251 logger.disabled = 0
252 hlist = cp.get(sectname, "handlers")
253 if len(hlist):
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000254 hlist = hlist.split(",")
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000255 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000256 for hand in hlist:
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000257 logger.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000258
259 #Disable any old loggers. There's no point deleting
260 #them as other threads may continue to hold references
261 #and by disabling them, you stop them doing any logging.
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000262 #However, don't disable children of named loggers, as that's
263 #probably not what was intended by the user.
Vinay Sajip989b69a2006-01-16 21:28:37 +0000264 for log in existing:
Vinay Sajip95dd03b2007-11-11 14:27:30 +0000265 logger = root.manager.loggerDict[log]
266 if log in child_loggers:
267 logger.level = logging.NOTSET
268 logger.handlers = []
269 logger.propagate = 1
Vinay Sajipbb2dad82013-03-23 11:18:10 +0000270 else:
271 logger.disabled = disable_existing_loggers
Vinay Sajip989b69a2006-01-16 21:28:37 +0000272
273
Vinay Sajip28c382f2010-02-04 18:48:53 +0000274
275IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
276
277
278def valid_ident(s):
279 m = IDENTIFIER.match(s)
280 if not m:
281 raise ValueError('Not a valid Python identifier: %r' % s)
282 return True
283
284
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000285class ConvertingMixin(object):
286 """For ConvertingXXX's, this mixin class provides common functions"""
287
288 def convert_with_key(self, key, value, replace=True):
289 result = self.configurator.convert(value)
290 #If the converted value is different, save for next time
291 if value is not result:
292 if replace:
293 self[key] = result
294 if type(result) in (ConvertingDict, ConvertingList,
295 ConvertingTuple):
296 result.parent = self
297 result.key = key
298 return result
299
300 def convert(self, value):
301 result = self.configurator.convert(value)
302 if value is not result:
303 if type(result) in (ConvertingDict, ConvertingList,
304 ConvertingTuple):
305 result.parent = self
306 return result
307
308
Vinay Sajip28c382f2010-02-04 18:48:53 +0000309# The ConvertingXXX classes are wrappers around standard Python containers,
310# and they serve to convert any suitable values in the container. The
311# conversion converts base dicts, lists and tuples to their wrapped
312# equivalents, whereas strings which match a conversion format are converted
313# appropriately.
314#
315# Each wrapper should have a configurator attribute holding the actual
316# configurator to use for conversion.
317
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000318class ConvertingDict(dict, ConvertingMixin):
Vinay Sajip28c382f2010-02-04 18:48:53 +0000319 """A converting dictionary wrapper."""
320
321 def __getitem__(self, key):
322 value = dict.__getitem__(self, key)
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000323 return self.convert_with_key(key, value)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000324
325 def get(self, key, default=None):
326 value = dict.get(self, key, default)
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000327 return self.convert_with_key(key, value)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000328
329 def pop(self, key, default=None):
330 value = dict.pop(self, key, default)
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000331 return self.convert_with_key(key, value, replace=False)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000332
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000333class ConvertingList(list, ConvertingMixin):
Vinay Sajip28c382f2010-02-04 18:48:53 +0000334 """A converting list wrapper."""
335 def __getitem__(self, key):
336 value = list.__getitem__(self, key)
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000337 return self.convert_with_key(key, value)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000338
339 def pop(self, idx=-1):
340 value = list.pop(self, idx)
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000341 return self.convert(value)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000342
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000343class ConvertingTuple(tuple, ConvertingMixin):
Vinay Sajip28c382f2010-02-04 18:48:53 +0000344 """A converting tuple wrapper."""
345 def __getitem__(self, key):
346 value = tuple.__getitem__(self, key)
Vinay Sajip9cdf2d02014-01-30 20:22:01 +0000347 # Can't replace a tuple entry.
348 return self.convert_with_key(key, value, replace=False)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000349
350class BaseConfigurator(object):
351 """
352 The configurator base class which defines some useful defaults.
353 """
354
355 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
356
357 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
358 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
359 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
360 DIGIT_PATTERN = re.compile(r'^\d+$')
361
362 value_converters = {
363 'ext' : 'ext_convert',
364 'cfg' : 'cfg_convert',
365 }
366
367 # We might want to use a different one, e.g. importlib
368 importer = __import__
369
370 def __init__(self, config):
371 self.config = ConvertingDict(config)
372 self.config.configurator = self
Vinay Sajip1e76e862013-03-25 23:37:41 +0000373 # Issue 12718: winpdb replaces __import__ with a Python function, which
374 # ends up being treated as a bound method. To avoid problems, we
375 # set the importer on the instance, but leave it defined in the class
376 # so existing code doesn't break
377 if type(__import__) == types.FunctionType:
378 self.importer = __import__
Vinay Sajip28c382f2010-02-04 18:48:53 +0000379
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)
Vinay Sajip3b4849a2010-06-02 10:05:31 +0000387 try:
388 found = self.importer(used)
389 for frag in name:
390 used += '.' + frag
391 try:
392 found = getattr(found, frag)
393 except AttributeError:
394 self.importer(used)
395 found = getattr(found, frag)
396 return found
397 except ImportError:
398 e, tb = sys.exc_info()[1:]
399 v = ValueError('Cannot resolve %r: %s' % (s, e))
400 v.__cause__, v.__traceback__ = e, tb
401 raise v
Vinay Sajip28c382f2010-02-04 18:48:53 +0000402
403 def ext_convert(self, value):
404 """Default converter for the ext:// protocol."""
405 return self.resolve(value)
406
407 def cfg_convert(self, value):
408 """Default converter for the cfg:// protocol."""
409 rest = value
410 m = self.WORD_PATTERN.match(rest)
411 if m is None:
412 raise ValueError("Unable to convert %r" % value)
413 else:
414 rest = rest[m.end():]
415 d = self.config[m.groups()[0]]
416 #print d, rest
417 while rest:
418 m = self.DOT_PATTERN.match(rest)
419 if m:
420 d = d[m.groups()[0]]
421 else:
422 m = self.INDEX_PATTERN.match(rest)
423 if m:
424 idx = m.groups()[0]
425 if not self.DIGIT_PATTERN.match(idx):
426 d = d[idx]
427 else:
428 try:
429 n = int(idx) # try as number first (most likely)
430 d = d[n]
431 except TypeError:
432 d = d[idx]
433 if m:
434 rest = rest[m.end():]
435 else:
436 raise ValueError('Unable to convert '
437 '%r at %r' % (value, rest))
438 #rest should be empty
439 return d
440
441 def convert(self, value):
442 """
443 Convert values to an appropriate type. dicts, lists and tuples are
444 replaced by their converting alternatives. Strings are checked to
445 see if they have a conversion format and are converted if they do.
446 """
447 if not isinstance(value, ConvertingDict) and isinstance(value, dict):
448 value = ConvertingDict(value)
449 value.configurator = self
450 elif not isinstance(value, ConvertingList) and isinstance(value, list):
451 value = ConvertingList(value)
452 value.configurator = self
453 elif not isinstance(value, ConvertingTuple) and\
454 isinstance(value, tuple):
455 value = ConvertingTuple(value)
456 value.configurator = self
457 elif isinstance(value, basestring): # str for py3k
458 m = self.CONVERT_PATTERN.match(value)
459 if m:
460 d = m.groupdict()
461 prefix = d['prefix']
462 converter = self.value_converters.get(prefix, None)
463 if converter:
464 suffix = d['suffix']
465 converter = getattr(self, converter)
466 value = converter(suffix)
467 return value
468
469 def configure_custom(self, config):
470 """Configure an object with a user-supplied factory."""
471 c = config.pop('()')
472 if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
473 c = self.resolve(c)
474 props = config.pop('.', None)
475 # Check for valid identifiers
476 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
477 result = c(**kwargs)
478 if props:
479 for name, value in props.items():
480 setattr(result, name, value)
481 return result
482
Vinay Sajip1adbee22010-03-06 15:56:03 +0000483 def as_tuple(self, value):
484 """Utility function which converts lists to tuples."""
485 if isinstance(value, list):
486 value = tuple(value)
487 return value
488
Vinay Sajip28c382f2010-02-04 18:48:53 +0000489class DictConfigurator(BaseConfigurator):
490 """
491 Configure logging using a dictionary-like object to describe the
492 configuration.
493 """
494
495 def configure(self):
496 """Do the configuration."""
497
498 config = self.config
Vinay Sajipd45a2782010-03-06 15:12:08 +0000499 if 'version' not in config:
500 raise ValueError("dictionary doesn't specify a version")
501 if config['version'] != 1:
502 raise ValueError("Unsupported version: %s" % config['version'])
Vinay Sajip28c382f2010-02-04 18:48:53 +0000503 incremental = config.pop('incremental', False)
504 EMPTY_DICT = {}
505 logging._acquireLock()
506 try:
507 if incremental:
508 handlers = config.get('handlers', EMPTY_DICT)
509 for name in handlers:
510 if name not in logging._handlers:
511 raise ValueError('No handler found with '
512 'name %r' % name)
513 else:
514 try:
515 handler = logging._handlers[name]
516 handler_config = handlers[name]
517 level = handler_config.get('level', None)
518 if level:
519 handler.setLevel(logging._checkLevel(level))
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000520 except StandardError as e:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000521 raise ValueError('Unable to configure handler '
522 '%r: %s' % (name, e))
523 loggers = config.get('loggers', EMPTY_DICT)
524 for name in loggers:
525 try:
526 self.configure_logger(name, loggers[name], True)
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000527 except StandardError as e:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000528 raise ValueError('Unable to configure logger '
529 '%r: %s' % (name, e))
530 root = config.get('root', None)
531 if root:
532 try:
533 self.configure_root(root, True)
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000534 except StandardError as e:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000535 raise ValueError('Unable to configure root '
536 'logger: %s' % e)
537 else:
538 disable_existing = config.pop('disable_existing_loggers', True)
539
540 logging._handlers.clear()
541 del logging._handlerList[:]
542
543 # Do formatters first - they don't refer to anything else
544 formatters = config.get('formatters', EMPTY_DICT)
545 for name in formatters:
546 try:
547 formatters[name] = self.configure_formatter(
548 formatters[name])
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000549 except StandardError as e:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000550 raise ValueError('Unable to configure '
551 'formatter %r: %s' % (name, e))
552 # Next, do filters - they don't refer to anything else, either
553 filters = config.get('filters', EMPTY_DICT)
554 for name in filters:
555 try:
556 filters[name] = self.configure_filter(filters[name])
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000557 except StandardError as e:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000558 raise ValueError('Unable to configure '
559 'filter %r: %s' % (name, e))
560
561 # Next, do handlers - they refer to formatters and filters
562 # As handlers can refer to other handlers, sort the keys
563 # to allow a deterministic order of configuration
564 handlers = config.get('handlers', EMPTY_DICT)
Vinay Sajip8651a512013-03-22 15:19:24 +0000565 deferred = []
Vinay Sajip28c382f2010-02-04 18:48:53 +0000566 for name in sorted(handlers):
567 try:
568 handler = self.configure_handler(handlers[name])
569 handler.name = name
570 handlers[name] = handler
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000571 except StandardError as e:
Vinay Sajip8651a512013-03-22 15:19:24 +0000572 if 'target not configured yet' in str(e):
573 deferred.append(name)
574 else:
575 raise ValueError('Unable to configure handler '
576 '%r: %s' % (name, e))
577
578 # Now do any that were deferred
579 for name in deferred:
580 try:
581 handler = self.configure_handler(handlers[name])
582 handler.name = name
583 handlers[name] = handler
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000584 except StandardError as e:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000585 raise ValueError('Unable to configure handler '
586 '%r: %s' % (name, e))
Vinay Sajip8651a512013-03-22 15:19:24 +0000587
Vinay Sajip28c382f2010-02-04 18:48:53 +0000588 # Next, do loggers - they refer to handlers and filters
589
590 #we don't want to lose the existing loggers,
591 #since other threads may have pointers to them.
592 #existing is set to contain all existing loggers,
593 #and as we go through the new configuration we
594 #remove any which are configured. At the end,
595 #what's left in existing is the set of loggers
596 #which were in the previous configuration but
597 #which are not in the new configuration.
598 root = logging.root
599 existing = root.manager.loggerDict.keys()
600 #The list needs to be sorted so that we can
601 #avoid disabling child loggers of explicitly
602 #named loggers. With a sorted list it is easier
603 #to find the child loggers.
Vinay Sajipcaf02722011-11-07 08:43:51 +0000604 existing.sort()
Vinay Sajip28c382f2010-02-04 18:48:53 +0000605 #We'll keep the list of existing loggers
606 #which are children of named loggers here...
607 child_loggers = []
608 #now set up the new ones...
609 loggers = config.get('loggers', EMPTY_DICT)
610 for name in loggers:
Vinay Sajipcaf02722011-11-07 08:43:51 +0000611 name = _encoded(name)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000612 if name in existing:
613 i = existing.index(name)
614 prefixed = name + "."
615 pflen = len(prefixed)
616 num_existing = len(existing)
617 i = i + 1 # look at the entry after name
618 while (i < num_existing) and\
619 (existing[i][:pflen] == prefixed):
620 child_loggers.append(existing[i])
621 i = i + 1
622 existing.remove(name)
623 try:
624 self.configure_logger(name, loggers[name])
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000625 except StandardError as e:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000626 raise ValueError('Unable to configure logger '
627 '%r: %s' % (name, e))
628
629 #Disable any old loggers. There's no point deleting
630 #them as other threads may continue to hold references
631 #and by disabling them, you stop them doing any logging.
632 #However, don't disable children of named loggers, as that's
633 #probably not what was intended by the user.
634 for log in existing:
635 logger = root.manager.loggerDict[log]
636 if log in child_loggers:
637 logger.level = logging.NOTSET
638 logger.handlers = []
639 logger.propagate = True
640 elif disable_existing:
641 logger.disabled = True
642
643 # And finally, do the root logger
644 root = config.get('root', None)
645 if root:
646 try:
647 self.configure_root(root)
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000648 except StandardError as e:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000649 raise ValueError('Unable to configure root '
650 'logger: %s' % e)
651 finally:
652 logging._releaseLock()
653
654 def configure_formatter(self, config):
655 """Configure a formatter from a dictionary."""
656 if '()' in config:
657 factory = config['()'] # for use in exception handler
658 try:
659 result = self.configure_custom(config)
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000660 except TypeError as te:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000661 if "'format'" not in str(te):
662 raise
663 #Name of parameter changed from fmt to format.
664 #Retry with old name.
665 #This is so that code can be used with older Python versions
666 #(e.g. by Django)
667 config['fmt'] = config.pop('format')
668 config['()'] = factory
669 result = self.configure_custom(config)
670 else:
671 fmt = config.get('format', None)
672 dfmt = config.get('datefmt', None)
673 result = logging.Formatter(fmt, dfmt)
674 return result
675
676 def configure_filter(self, config):
677 """Configure a filter from a dictionary."""
678 if '()' in config:
679 result = self.configure_custom(config)
680 else:
681 name = config.get('name', '')
682 result = logging.Filter(name)
683 return result
684
685 def add_filters(self, filterer, filters):
686 """Add filters to a filterer from a list of names."""
687 for f in filters:
688 try:
689 filterer.addFilter(self.config['filters'][f])
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000690 except StandardError as e:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000691 raise ValueError('Unable to add filter %r: %s' % (f, e))
692
693 def configure_handler(self, config):
694 """Configure a handler from a dictionary."""
695 formatter = config.pop('formatter', None)
696 if formatter:
697 try:
698 formatter = self.config['formatters'][formatter]
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000699 except StandardError as e:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000700 raise ValueError('Unable to set formatter '
701 '%r: %s' % (formatter, e))
702 level = config.pop('level', None)
703 filters = config.pop('filters', None)
704 if '()' in config:
705 c = config.pop('()')
706 if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
707 c = self.resolve(c)
708 factory = c
709 else:
Vinay Sajip8651a512013-03-22 15:19:24 +0000710 cname = config.pop('class')
711 klass = self.resolve(cname)
Vinay Sajip28c382f2010-02-04 18:48:53 +0000712 #Special case for handler which refers to another handler
713 if issubclass(klass, logging.handlers.MemoryHandler) and\
714 'target' in config:
715 try:
Vinay Sajip8651a512013-03-22 15:19:24 +0000716 th = self.config['handlers'][config['target']]
717 if not isinstance(th, logging.Handler):
718 config['class'] = cname # restore for deferred configuration
719 raise StandardError('target not configured yet')
720 config['target'] = th
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000721 except StandardError as e:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000722 raise ValueError('Unable to set target handler '
723 '%r: %s' % (config['target'], e))
Vinay Sajip1adbee22010-03-06 15:56:03 +0000724 elif issubclass(klass, logging.handlers.SMTPHandler) and\
725 'mailhost' in config:
726 config['mailhost'] = self.as_tuple(config['mailhost'])
727 elif issubclass(klass, logging.handlers.SysLogHandler) and\
728 'address' in config:
729 config['address'] = self.as_tuple(config['address'])
Vinay Sajip28c382f2010-02-04 18:48:53 +0000730 factory = klass
731 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
732 try:
733 result = factory(**kwargs)
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000734 except TypeError as te:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000735 if "'stream'" not in str(te):
736 raise
737 #The argument name changed from strm to stream
738 #Retry with old name.
739 #This is so that code can be used with older Python versions
740 #(e.g. by Django)
741 kwargs['strm'] = kwargs.pop('stream')
742 result = factory(**kwargs)
743 if formatter:
744 result.setFormatter(formatter)
745 if level is not None:
746 result.setLevel(logging._checkLevel(level))
747 if filters:
748 self.add_filters(result, filters)
749 return result
750
751 def add_handlers(self, logger, handlers):
752 """Add handlers to a logger from a list of names."""
753 for h in handlers:
754 try:
755 logger.addHandler(self.config['handlers'][h])
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000756 except StandardError as e:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000757 raise ValueError('Unable to add handler %r: %s' % (h, e))
758
759 def common_logger_config(self, logger, config, incremental=False):
760 """
761 Perform configuration which is common to root and non-root loggers.
762 """
763 level = config.get('level', None)
764 if level is not None:
765 logger.setLevel(logging._checkLevel(level))
766 if not incremental:
767 #Remove any existing handlers
768 for h in logger.handlers[:]:
769 logger.removeHandler(h)
770 handlers = config.get('handlers', None)
771 if handlers:
772 self.add_handlers(logger, handlers)
773 filters = config.get('filters', None)
774 if filters:
775 self.add_filters(logger, filters)
776
777 def configure_logger(self, name, config, incremental=False):
778 """Configure a non-root logger from a dictionary."""
779 logger = logging.getLogger(name)
780 self.common_logger_config(logger, config, incremental)
781 propagate = config.get('propagate', None)
782 if propagate is not None:
783 logger.propagate = propagate
784
785 def configure_root(self, config, incremental=False):
786 """Configure a root logger from a dictionary."""
787 root = logging.getLogger()
788 self.common_logger_config(root, config, incremental)
789
790dictConfigClass = DictConfigurator
791
792def dictConfig(config):
793 """Configure logging using a dictionary."""
794 dictConfigClass(config).configure()
795
796
Guido van Rossum57102f82002-11-13 16:15:58 +0000797def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
798 """
799 Start up a socket server on the specified port, and listen for new
800 configurations.
801
802 These will be sent as a file suitable for processing by fileConfig().
803 Returns a Thread object on which you can call start() to start the server,
804 and which you can join() when appropriate. To stop the server, call
805 stopListening().
806 """
807 if not thread:
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000808 raise NotImplementedError("listen() needs threading to work")
Guido van Rossum57102f82002-11-13 16:15:58 +0000809
810 class ConfigStreamHandler(StreamRequestHandler):
811 """
812 Handler for a logging configuration request.
813
814 It expects a completely new logging configuration and uses fileConfig
815 to install it.
816 """
817 def handle(self):
818 """
819 Handle a request.
820
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000821 Each request is expected to be a 4-byte length, packed using
822 struct.pack(">L", n), followed by the config file.
823 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000824 """
825 import tempfile
826 try:
827 conn = self.connection
828 chunk = conn.recv(4)
829 if len(chunk) == 4:
830 slen = struct.unpack(">L", chunk)[0]
831 chunk = self.connection.recv(slen)
832 while len(chunk) < slen:
833 chunk = chunk + conn.recv(slen - len(chunk))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000834 try:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000835 import json
836 d =json.loads(chunk)
837 assert isinstance(d, dict)
838 dictConfig(d)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000839 except:
Vinay Sajip28c382f2010-02-04 18:48:53 +0000840 #Apply new configuration.
841
842 file = cStringIO.StringIO(chunk)
843 try:
844 fileConfig(file)
845 except (KeyboardInterrupt, SystemExit):
846 raise
847 except:
848 traceback.print_exc()
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000849 if self.server.ready:
850 self.server.ready.set()
Vinay Sajip79ce12e2014-02-11 11:32:40 +0000851 except socket.error as e:
852 if e.errno != RESET_ERROR:
Guido van Rossum57102f82002-11-13 16:15:58 +0000853 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000854
855 class ConfigSocketReceiver(ThreadingTCPServer):
856 """
857 A simple TCP socket-based logging config receiver.
858 """
859
860 allow_reuse_address = 1
861
862 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000863 handler=None, ready=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000864 ThreadingTCPServer.__init__(self, (host, port), handler)
865 logging._acquireLock()
866 self.abort = 0
867 logging._releaseLock()
868 self.timeout = 1
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000869 self.ready = ready
Guido van Rossum57102f82002-11-13 16:15:58 +0000870
871 def serve_until_stopped(self):
872 import select
873 abort = 0
874 while not abort:
875 rd, wr, ex = select.select([self.socket.fileno()],
876 [], [],
877 self.timeout)
878 if rd:
879 self.handle_request()
880 logging._acquireLock()
881 abort = self.abort
882 logging._releaseLock()
Brian Curtine27731d2010-10-31 04:44:04 +0000883 self.socket.close()
Guido van Rossum57102f82002-11-13 16:15:58 +0000884
Benjamin Peterson239f1382010-02-06 22:08:15 +0000885 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000886
Benjamin Peterson239f1382010-02-06 22:08:15 +0000887 def __init__(self, rcvr, hdlr, port):
888 super(Server, self).__init__()
889 self.rcvr = rcvr
890 self.hdlr = hdlr
891 self.port = port
892 self.ready = threading.Event()
893
894 def run(self):
Vinay Sajipcfc43e92010-02-08 21:18:15 +0000895 server = self.rcvr(port=self.port, handler=self.hdlr,
896 ready=self.ready)
Vinay Sajip27a13702010-05-03 15:11:53 +0000897 if self.port == 0:
898 self.port = server.server_address[1]
Benjamin Peterson239f1382010-02-06 22:08:15 +0000899 self.ready.set()
900 global _listener
901 logging._acquireLock()
902 _listener = server
903 logging._releaseLock()
904 server.serve_until_stopped()
905
906 return Server(ConfigSocketReceiver, ConfigStreamHandler, port)
Guido van Rossum57102f82002-11-13 16:15:58 +0000907
908def stopListening():
909 """
910 Stop the listening server which was created with a call to listen().
911 """
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000912 global _listener
Vinay Sajip3eac5912010-09-25 17:48:25 +0000913 logging._acquireLock()
914 try:
915 if _listener:
916 _listener.abort = 1
917 _listener = None
918 finally:
Guido van Rossum57102f82002-11-13 16:15:58 +0000919 logging._releaseLock()