blob: 8a99923bf31ef9990bb2b89bd80ade1aa96e835d [file] [log] [blame]
Vinay Sajip71dcb282014-03-20 13:03:17 +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 Sajip71dcb282014-03-20 13:03:17 +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 Sajip71dcb282014-03-20 13:03:17 +000027import errno
Florent Xicluna5252f9f2011-11-07 19:43:05 +010028import io
Vinay Sajip71dcb282014-03-20 13:03:17 +000029import logging
30import logging.handlers
31import re
32import struct
33import sys
34import traceback
Vinay Sajip612df8e2005-02-18 11:54:46 +000035
36try:
Georg Brandl2067bfd2008-05-25 13:05:15 +000037 import _thread as thread
Vinay Sajip612df8e2005-02-18 11:54:46 +000038 import threading
Brett Cannoncd171c82013-07-04 17:43:24 -040039except ImportError: #pragma: no cover
Vinay Sajip612df8e2005-02-18 11:54:46 +000040 thread = None
Guido van Rossum57102f82002-11-13 16:15:58 +000041
Alexandre Vassalottice261952008-05-12 02:31:37 +000042from socketserver import ThreadingTCPServer, StreamRequestHandler
Guido van Rossum57102f82002-11-13 16:15:58 +000043
44
45DEFAULT_LOGGING_CONFIG_PORT = 9030
46
Vinay Sajip71dcb282014-03-20 13:03:17 +000047RESET_ERROR = errno.ECONNRESET
Vinay Sajip326441e2004-02-20 13:16:36 +000048
Guido van Rossum57102f82002-11-13 16:15:58 +000049#
50# The following code implements a socket listener for on-the-fly
51# reconfiguration of logging.
52#
53# _listener holds the server object doing the listening
54_listener = None
55
Georg Brandl472f2e22009-06-08 08:58:54 +000056def fileConfig(fname, defaults=None, disable_existing_loggers=True):
Guido van Rossum57102f82002-11-13 16:15:58 +000057 """
58 Read the logging configuration from a ConfigParser-format file.
59
60 This can be called several times from an application, allowing an end user
61 the ability to select from various pre-canned configurations (if the
62 developer provides a mechanism to present the choices and load the chosen
63 configuration).
Guido van Rossum57102f82002-11-13 16:15:58 +000064 """
Alexandre Vassalotti1d1eaa42008-05-14 22:59:42 +000065 import configparser
Guido van Rossum57102f82002-11-13 16:15:58 +000066
Vinay Sajipcf9e2f22012-10-09 09:06:03 +010067 if isinstance(fname, configparser.RawConfigParser):
68 cp = fname
Guido van Rossum57102f82002-11-13 16:15:58 +000069 else:
Vinay Sajipcf9e2f22012-10-09 09:06:03 +010070 cp = configparser.ConfigParser(defaults)
71 if hasattr(fname, 'readline'):
72 cp.read_file(fname)
73 else:
74 cp.read(fname)
Vinay Sajip989b69a2006-01-16 21:28:37 +000075
76 formatters = _create_formatters(cp)
77
78 # critical section
Guido van Rossum57102f82002-11-13 16:15:58 +000079 logging._acquireLock()
80 try:
Vinay Sajip989b69a2006-01-16 21:28:37 +000081 logging._handlers.clear()
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000082 del logging._handlerList[:]
Vinay Sajip989b69a2006-01-16 21:28:37 +000083 # Handlers add themselves to logging._handlers
84 handlers = _install_handlers(cp, formatters)
Benjamin Petersonfea6a942008-07-02 16:11:42 +000085 _install_loggers(cp, handlers, disable_existing_loggers)
Guido van Rossum57102f82002-11-13 16:15:58 +000086 finally:
87 logging._releaseLock()
88
Vinay Sajip989b69a2006-01-16 21:28:37 +000089
Vinay Sajip7a7160b2006-01-20 18:28:03 +000090def _resolve(name):
91 """Resolve a dotted name to a global object."""
Neal Norwitz9d72bb42007-04-17 08:48:32 +000092 name = name.split('.')
Vinay Sajip7a7160b2006-01-20 18:28:03 +000093 used = name.pop(0)
94 found = __import__(used)
95 for n in name:
96 used = used + '.' + n
97 try:
98 found = getattr(found, n)
99 except AttributeError:
100 __import__(used)
101 found = getattr(found, n)
102 return found
103
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000104def _strip_spaces(alist):
105 return map(lambda x: x.strip(), alist)
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000106
Vinay Sajip989b69a2006-01-16 21:28:37 +0000107def _create_formatters(cp):
108 """Create and return formatters"""
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000109 flist = cp["formatters"]["keys"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000110 if not len(flist):
111 return {}
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000112 flist = flist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000113 flist = _strip_spaces(flist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000114 formatters = {}
115 for form in flist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000116 sectname = "formatter_%s" % form
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000117 fs = cp.get(sectname, "format", raw=True, fallback=None)
118 dfs = cp.get(sectname, "datefmt", raw=True, fallback=None)
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100119 stl = cp.get(sectname, "style", raw=True, fallback='%')
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000120 c = logging.Formatter
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000121 class_name = cp[sectname].get("class")
122 if class_name:
123 c = _resolve(class_name)
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100124 f = c(fs, dfs, stl)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000125 formatters[form] = f
126 return formatters
127
128
129def _install_handlers(cp, formatters):
130 """Install and return handlers"""
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000131 hlist = cp["handlers"]["keys"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000132 if not len(hlist):
133 return {}
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000134 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000135 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000136 handlers = {}
137 fixups = [] #for inter-handler references
138 for hand in hlist:
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000139 section = cp["handler_%s" % hand]
140 klass = section["class"]
141 fmt = section.get("formatter", "")
Georg Brandl3dbca812008-07-23 16:10:53 +0000142 try:
143 klass = eval(klass, vars(logging))
144 except (AttributeError, NameError):
145 klass = _resolve(klass)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000146 args = section["args"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000147 args = eval(args, vars(logging))
Neal Norwitzd9108552006-03-17 08:00:19 +0000148 h = klass(*args)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000149 if "level" in section:
150 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700151 h.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000152 if len(fmt):
153 h.setFormatter(formatters[fmt])
Benjamin Peterson41181742008-07-02 20:22:54 +0000154 if issubclass(klass, logging.handlers.MemoryHandler):
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000155 target = section.get("target", "")
Vinay Sajip989b69a2006-01-16 21:28:37 +0000156 if len(target): #the target handler may not be loaded yet, so keep for later...
157 fixups.append((h, target))
158 handlers[hand] = h
159 #now all handlers are loaded, fixup inter-handler references...
160 for h, t in fixups:
161 h.setTarget(handlers[t])
162 return handlers
163
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000164def _handle_existing_loggers(existing, child_loggers, disable_existing):
165 """
166 When (re)configuring logging, handle loggers which were in the previous
167 configuration but are not in the new configuration. There's no point
168 deleting them as other threads may continue to hold references to them;
169 and by disabling them, you stop them doing any logging.
Vinay Sajip989b69a2006-01-16 21:28:37 +0000170
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000171 However, don't disable children of named loggers, as that's probably not
172 what was intended by the user. Also, allow existing loggers to NOT be
173 disabled if disable_existing is false.
174 """
175 root = logging.root
176 for log in existing:
177 logger = root.manager.loggerDict[log]
178 if log in child_loggers:
179 logger.level = logging.NOTSET
180 logger.handlers = []
181 logger.propagate = True
Vinay Sajip68b4cc82013-03-23 11:18:45 +0000182 else:
183 logger.disabled = disable_existing
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000184
185def _install_loggers(cp, handlers, disable_existing):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000186 """Create and install loggers"""
187
188 # configure the root first
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000189 llist = cp["loggers"]["keys"]
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000190 llist = llist.split(",")
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000191 llist = list(map(lambda x: x.strip(), llist))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000192 llist.remove("root")
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000193 section = cp["logger_root"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000194 root = logging.root
195 log = root
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000196 if "level" in section:
197 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700198 log.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000199 for h in root.handlers[:]:
200 root.removeHandler(h)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000201 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000202 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000203 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000204 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000205 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000206 log.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000207
208 #and now the others...
209 #we don't want to lose the existing loggers,
210 #since other threads may have pointers to them.
211 #existing is set to contain all existing loggers,
212 #and as we go through the new configuration we
213 #remove any which are configured. At the end,
214 #what's left in existing is the set of loggers
215 #which were in the previous configuration but
216 #which are not in the new configuration.
Guido van Rossum8b8a5432007-02-12 00:07:01 +0000217 existing = list(root.manager.loggerDict.keys())
Christian Heimes96f31632007-11-12 01:32:03 +0000218 #The list needs to be sorted so that we can
219 #avoid disabling child loggers of explicitly
220 #named loggers. With a sorted list it is easier
221 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100222 existing.sort()
Christian Heimes96f31632007-11-12 01:32:03 +0000223 #We'll keep the list of existing loggers
224 #which are children of named loggers here...
225 child_loggers = []
Vinay Sajip989b69a2006-01-16 21:28:37 +0000226 #now set up the new ones...
227 for log in llist:
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000228 section = cp["logger_%s" % log]
229 qn = section["qualname"]
230 propagate = section.getint("propagate", fallback=1)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000231 logger = logging.getLogger(qn)
232 if qn in existing:
Vinay Sajip3f84b072011-03-07 17:49:33 +0000233 i = existing.index(qn) + 1 # start with the entry after qn
Christian Heimes96f31632007-11-12 01:32:03 +0000234 prefixed = qn + "."
235 pflen = len(prefixed)
236 num_existing = len(existing)
Vinay Sajip3f84b072011-03-07 17:49:33 +0000237 while i < num_existing:
238 if existing[i][:pflen] == prefixed:
239 child_loggers.append(existing[i])
240 i += 1
Vinay Sajip989b69a2006-01-16 21:28:37 +0000241 existing.remove(qn)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000242 if "level" in section:
243 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700244 logger.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000245 for h in logger.handlers[:]:
246 logger.removeHandler(h)
247 logger.propagate = propagate
248 logger.disabled = 0
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000249 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000250 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000251 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000252 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000253 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000254 logger.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000255
256 #Disable any old loggers. There's no point deleting
257 #them as other threads may continue to hold references
258 #and by disabling them, you stop them doing any logging.
Christian Heimes96f31632007-11-12 01:32:03 +0000259 #However, don't disable children of named loggers, as that's
260 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000261 #for log in existing:
262 # logger = root.manager.loggerDict[log]
263 # if log in child_loggers:
264 # logger.level = logging.NOTSET
265 # logger.handlers = []
266 # logger.propagate = 1
267 # elif disable_existing_loggers:
268 # logger.disabled = 1
269 _handle_existing_loggers(existing, child_loggers, disable_existing)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000270
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000271IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
272
273
274def valid_ident(s):
275 m = IDENTIFIER.match(s)
276 if not m:
277 raise ValueError('Not a valid Python identifier: %r' % s)
278 return True
279
280
Vinay Sajipb1698d42014-03-20 13:14:39 +0000281class ConvertingMixin(object):
282 """For ConvertingXXX's, this mixin class provides common functions"""
283
284 def convert_with_key(self, key, value, replace=True):
285 result = self.configurator.convert(value)
286 #If the converted value is different, save for next time
287 if value is not result:
288 if replace:
289 self[key] = result
290 if type(result) in (ConvertingDict, ConvertingList,
291 ConvertingTuple):
292 result.parent = self
293 result.key = key
294 return result
295
296 def convert(self, value):
297 result = self.configurator.convert(value)
298 if value is not result:
299 if type(result) in (ConvertingDict, ConvertingList,
300 ConvertingTuple):
301 result.parent = self
302 return result
303
304
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000305# The ConvertingXXX classes are wrappers around standard Python containers,
306# and they serve to convert any suitable values in the container. The
307# conversion converts base dicts, lists and tuples to their wrapped
308# equivalents, whereas strings which match a conversion format are converted
309# appropriately.
310#
311# Each wrapper should have a configurator attribute holding the actual
312# configurator to use for conversion.
313
Vinay Sajipb1698d42014-03-20 13:14:39 +0000314class ConvertingDict(dict, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000315 """A converting dictionary wrapper."""
316
317 def __getitem__(self, key):
318 value = dict.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000319 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000320
321 def get(self, key, default=None):
322 value = dict.get(self, key, default)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000323 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000324
325 def pop(self, key, default=None):
326 value = dict.pop(self, key, default)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000327 return self.convert_with_key(key, value, replace=False)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000328
Vinay Sajipb1698d42014-03-20 13:14:39 +0000329class ConvertingList(list, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000330 """A converting list wrapper."""
331 def __getitem__(self, key):
332 value = list.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000333 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000334
335 def pop(self, idx=-1):
336 value = list.pop(self, idx)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000337 return self.convert(value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000338
Vinay Sajipb1698d42014-03-20 13:14:39 +0000339class ConvertingTuple(tuple, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000340 """A converting tuple wrapper."""
341 def __getitem__(self, key):
342 value = tuple.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000343 # Can't replace a tuple entry.
344 return self.convert_with_key(key, value, replace=False)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000345
346class BaseConfigurator(object):
347 """
348 The configurator base class which defines some useful defaults.
349 """
350
351 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
352
353 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
354 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
355 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
356 DIGIT_PATTERN = re.compile(r'^\d+$')
357
358 value_converters = {
359 'ext' : 'ext_convert',
360 'cfg' : 'cfg_convert',
361 }
362
363 # We might want to use a different one, e.g. importlib
Brett Cannonc2368502010-06-12 00:39:28 +0000364 importer = staticmethod(__import__)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000365
366 def __init__(self, config):
367 self.config = ConvertingDict(config)
368 self.config.configurator = self
369
370 def resolve(self, s):
371 """
372 Resolve strings to objects using standard import and attribute
373 syntax.
374 """
375 name = s.split('.')
376 used = name.pop(0)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000377 try:
378 found = self.importer(used)
379 for frag in name:
380 used += '.' + frag
381 try:
382 found = getattr(found, frag)
383 except AttributeError:
384 self.importer(used)
385 found = getattr(found, frag)
386 return found
387 except ImportError:
388 e, tb = sys.exc_info()[1:]
389 v = ValueError('Cannot resolve %r: %s' % (s, e))
390 v.__cause__, v.__traceback__ = e, tb
391 raise v
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000392
393 def ext_convert(self, value):
394 """Default converter for the ext:// protocol."""
395 return self.resolve(value)
396
397 def cfg_convert(self, value):
398 """Default converter for the cfg:// protocol."""
399 rest = value
400 m = self.WORD_PATTERN.match(rest)
401 if m is None:
402 raise ValueError("Unable to convert %r" % value)
403 else:
404 rest = rest[m.end():]
405 d = self.config[m.groups()[0]]
406 #print d, rest
407 while rest:
408 m = self.DOT_PATTERN.match(rest)
409 if m:
410 d = d[m.groups()[0]]
411 else:
412 m = self.INDEX_PATTERN.match(rest)
413 if m:
414 idx = m.groups()[0]
415 if not self.DIGIT_PATTERN.match(idx):
416 d = d[idx]
417 else:
418 try:
419 n = int(idx) # try as number first (most likely)
420 d = d[n]
421 except TypeError:
422 d = d[idx]
423 if m:
424 rest = rest[m.end():]
425 else:
426 raise ValueError('Unable to convert '
427 '%r at %r' % (value, rest))
428 #rest should be empty
429 return d
430
431 def convert(self, value):
432 """
433 Convert values to an appropriate type. dicts, lists and tuples are
434 replaced by their converting alternatives. Strings are checked to
435 see if they have a conversion format and are converted if they do.
436 """
437 if not isinstance(value, ConvertingDict) and isinstance(value, dict):
438 value = ConvertingDict(value)
439 value.configurator = self
440 elif not isinstance(value, ConvertingList) and isinstance(value, list):
441 value = ConvertingList(value)
442 value.configurator = self
443 elif not isinstance(value, ConvertingTuple) and\
444 isinstance(value, tuple):
445 value = ConvertingTuple(value)
446 value.configurator = self
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000447 elif isinstance(value, str): # str for py3k
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000448 m = self.CONVERT_PATTERN.match(value)
449 if m:
450 d = m.groupdict()
451 prefix = d['prefix']
452 converter = self.value_converters.get(prefix, None)
453 if converter:
454 suffix = d['suffix']
455 converter = getattr(self, converter)
456 value = converter(suffix)
457 return value
458
459 def configure_custom(self, config):
460 """Configure an object with a user-supplied factory."""
461 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200462 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000463 c = self.resolve(c)
464 props = config.pop('.', None)
465 # Check for valid identifiers
466 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
467 result = c(**kwargs)
468 if props:
469 for name, value in props.items():
470 setattr(result, name, value)
471 return result
472
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000473 def as_tuple(self, value):
474 """Utility function which converts lists to tuples."""
475 if isinstance(value, list):
476 value = tuple(value)
477 return value
478
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000479class DictConfigurator(BaseConfigurator):
480 """
481 Configure logging using a dictionary-like object to describe the
482 configuration.
483 """
484
485 def configure(self):
486 """Do the configuration."""
487
488 config = self.config
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000489 if 'version' not in config:
490 raise ValueError("dictionary doesn't specify a version")
491 if config['version'] != 1:
492 raise ValueError("Unsupported version: %s" % config['version'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000493 incremental = config.pop('incremental', False)
494 EMPTY_DICT = {}
495 logging._acquireLock()
496 try:
497 if incremental:
498 handlers = config.get('handlers', EMPTY_DICT)
499 for name in handlers:
500 if name not in logging._handlers:
501 raise ValueError('No handler found with '
502 'name %r' % name)
503 else:
504 try:
505 handler = logging._handlers[name]
506 handler_config = handlers[name]
507 level = handler_config.get('level', None)
508 if level:
509 handler.setLevel(logging._checkLevel(level))
510 except Exception as e:
511 raise ValueError('Unable to configure handler '
512 '%r: %s' % (name, e))
513 loggers = config.get('loggers', EMPTY_DICT)
514 for name in loggers:
515 try:
516 self.configure_logger(name, loggers[name], True)
517 except Exception as e:
518 raise ValueError('Unable to configure logger '
519 '%r: %s' % (name, e))
520 root = config.get('root', None)
521 if root:
522 try:
523 self.configure_root(root, True)
524 except Exception as e:
525 raise ValueError('Unable to configure root '
526 'logger: %s' % e)
527 else:
528 disable_existing = config.pop('disable_existing_loggers', True)
529
530 logging._handlers.clear()
531 del logging._handlerList[:]
532
533 # Do formatters first - they don't refer to anything else
534 formatters = config.get('formatters', EMPTY_DICT)
535 for name in formatters:
536 try:
537 formatters[name] = self.configure_formatter(
538 formatters[name])
539 except Exception as e:
540 raise ValueError('Unable to configure '
541 'formatter %r: %s' % (name, e))
542 # Next, do filters - they don't refer to anything else, either
543 filters = config.get('filters', EMPTY_DICT)
544 for name in filters:
545 try:
546 filters[name] = self.configure_filter(filters[name])
547 except Exception as e:
548 raise ValueError('Unable to configure '
549 'filter %r: %s' % (name, e))
550
551 # Next, do handlers - they refer to formatters and filters
552 # As handlers can refer to other handlers, sort the keys
553 # to allow a deterministic order of configuration
554 handlers = config.get('handlers', EMPTY_DICT)
Vinay Sajip3f885b52013-03-22 15:19:54 +0000555 deferred = []
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000556 for name in sorted(handlers):
557 try:
558 handler = self.configure_handler(handlers[name])
559 handler.name = name
560 handlers[name] = handler
561 except Exception as e:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000562 if 'target not configured yet' in str(e):
563 deferred.append(name)
564 else:
565 raise ValueError('Unable to configure handler '
566 '%r: %s' % (name, e))
567
568 # Now do any that were deferred
569 for name in deferred:
570 try:
571 handler = self.configure_handler(handlers[name])
572 handler.name = name
573 handlers[name] = handler
574 except Exception as e:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000575 raise ValueError('Unable to configure handler '
576 '%r: %s' % (name, e))
Vinay Sajip3f885b52013-03-22 15:19:54 +0000577
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000578 # Next, do loggers - they refer to handlers and filters
579
580 #we don't want to lose the existing loggers,
581 #since other threads may have pointers to them.
582 #existing is set to contain all existing loggers,
583 #and as we go through the new configuration we
584 #remove any which are configured. At the end,
585 #what's left in existing is the set of loggers
586 #which were in the previous configuration but
587 #which are not in the new configuration.
588 root = logging.root
589 existing = list(root.manager.loggerDict.keys())
590 #The list needs to be sorted so that we can
591 #avoid disabling child loggers of explicitly
592 #named loggers. With a sorted list it is easier
593 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100594 existing.sort()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000595 #We'll keep the list of existing loggers
596 #which are children of named loggers here...
597 child_loggers = []
598 #now set up the new ones...
599 loggers = config.get('loggers', EMPTY_DICT)
600 for name in loggers:
601 if name in existing:
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000602 i = existing.index(name) + 1 # look after name
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000603 prefixed = name + "."
604 pflen = len(prefixed)
605 num_existing = len(existing)
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000606 while i < num_existing:
607 if existing[i][:pflen] == prefixed:
608 child_loggers.append(existing[i])
609 i += 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000610 existing.remove(name)
611 try:
612 self.configure_logger(name, loggers[name])
613 except Exception as e:
614 raise ValueError('Unable to configure logger '
615 '%r: %s' % (name, e))
616
617 #Disable any old loggers. There's no point deleting
618 #them as other threads may continue to hold references
619 #and by disabling them, you stop them doing any logging.
620 #However, don't disable children of named loggers, as that's
621 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000622 #for log in existing:
623 # logger = root.manager.loggerDict[log]
624 # if log in child_loggers:
625 # logger.level = logging.NOTSET
626 # logger.handlers = []
627 # logger.propagate = True
628 # elif disable_existing:
629 # logger.disabled = True
630 _handle_existing_loggers(existing, child_loggers,
631 disable_existing)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000632
633 # And finally, do the root logger
634 root = config.get('root', None)
635 if root:
636 try:
637 self.configure_root(root)
638 except Exception as e:
639 raise ValueError('Unable to configure root '
640 'logger: %s' % e)
641 finally:
642 logging._releaseLock()
643
644 def configure_formatter(self, config):
645 """Configure a formatter from a dictionary."""
646 if '()' in config:
647 factory = config['()'] # for use in exception handler
648 try:
649 result = self.configure_custom(config)
650 except TypeError as te:
651 if "'format'" not in str(te):
652 raise
653 #Name of parameter changed from fmt to format.
654 #Retry with old name.
655 #This is so that code can be used with older Python versions
656 #(e.g. by Django)
657 config['fmt'] = config.pop('format')
658 config['()'] = factory
659 result = self.configure_custom(config)
660 else:
661 fmt = config.get('format', None)
662 dfmt = config.get('datefmt', None)
Vinay Sajip28421c62013-03-29 17:56:54 +0000663 style = config.get('style', '%')
Vinay Sajipddbd2ee2014-04-15 14:24:53 +0100664 cname = config.get('class', None)
665 if not cname:
666 c = logging.Formatter
667 else:
668 c = _resolve(cname)
669 result = c(fmt, dfmt, style)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000670 return result
671
672 def configure_filter(self, config):
673 """Configure a filter from a dictionary."""
674 if '()' in config:
675 result = self.configure_custom(config)
676 else:
677 name = config.get('name', '')
678 result = logging.Filter(name)
679 return result
680
681 def add_filters(self, filterer, filters):
682 """Add filters to a filterer from a list of names."""
683 for f in filters:
684 try:
685 filterer.addFilter(self.config['filters'][f])
686 except Exception as e:
687 raise ValueError('Unable to add filter %r: %s' % (f, e))
688
689 def configure_handler(self, config):
690 """Configure a handler from a dictionary."""
Vinay Sajip28421c62013-03-29 17:56:54 +0000691 config_copy = dict(config) # for restoring in case of error
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000692 formatter = config.pop('formatter', None)
693 if formatter:
694 try:
695 formatter = self.config['formatters'][formatter]
696 except Exception as e:
697 raise ValueError('Unable to set formatter '
698 '%r: %s' % (formatter, e))
699 level = config.pop('level', None)
700 filters = config.pop('filters', None)
701 if '()' in config:
702 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200703 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000704 c = self.resolve(c)
705 factory = c
706 else:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000707 cname = config.pop('class')
708 klass = self.resolve(cname)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000709 #Special case for handler which refers to another handler
710 if issubclass(klass, logging.handlers.MemoryHandler) and\
711 'target' in config:
712 try:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000713 th = self.config['handlers'][config['target']]
714 if not isinstance(th, logging.Handler):
Vinay Sajip28421c62013-03-29 17:56:54 +0000715 config.update(config_copy) # restore for deferred cfg
Vinay Sajip3f885b52013-03-22 15:19:54 +0000716 raise TypeError('target not configured yet')
717 config['target'] = th
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000718 except Exception as e:
719 raise ValueError('Unable to set target handler '
720 '%r: %s' % (config['target'], e))
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000721 elif issubclass(klass, logging.handlers.SMTPHandler) and\
722 'mailhost' in config:
723 config['mailhost'] = self.as_tuple(config['mailhost'])
724 elif issubclass(klass, logging.handlers.SysLogHandler) and\
725 'address' in config:
726 config['address'] = self.as_tuple(config['address'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000727 factory = klass
Vinay Sajip8d270232012-11-15 14:20:18 +0000728 props = config.pop('.', None)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000729 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
730 try:
731 result = factory(**kwargs)
732 except TypeError as te:
733 if "'stream'" not in str(te):
734 raise
735 #The argument name changed from strm to stream
736 #Retry with old name.
737 #This is so that code can be used with older Python versions
738 #(e.g. by Django)
739 kwargs['strm'] = kwargs.pop('stream')
740 result = factory(**kwargs)
741 if formatter:
742 result.setFormatter(formatter)
743 if level is not None:
744 result.setLevel(logging._checkLevel(level))
745 if filters:
746 self.add_filters(result, filters)
Vinay Sajip8d270232012-11-15 14:20:18 +0000747 if props:
748 for name, value in props.items():
749 setattr(result, name, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000750 return result
751
752 def add_handlers(self, logger, handlers):
753 """Add handlers to a logger from a list of names."""
754 for h in handlers:
755 try:
756 logger.addHandler(self.config['handlers'][h])
757 except Exception as e:
758 raise ValueError('Unable to add handler %r: %s' % (h, e))
759
760 def common_logger_config(self, logger, config, incremental=False):
761 """
762 Perform configuration which is common to root and non-root loggers.
763 """
764 level = config.get('level', None)
765 if level is not None:
766 logger.setLevel(logging._checkLevel(level))
767 if not incremental:
768 #Remove any existing handlers
769 for h in logger.handlers[:]:
770 logger.removeHandler(h)
771 handlers = config.get('handlers', None)
772 if handlers:
773 self.add_handlers(logger, handlers)
774 filters = config.get('filters', None)
775 if filters:
776 self.add_filters(logger, filters)
777
778 def configure_logger(self, name, config, incremental=False):
779 """Configure a non-root logger from a dictionary."""
780 logger = logging.getLogger(name)
781 self.common_logger_config(logger, config, incremental)
782 propagate = config.get('propagate', None)
783 if propagate is not None:
784 logger.propagate = propagate
785
786 def configure_root(self, config, incremental=False):
787 """Configure a root logger from a dictionary."""
788 root = logging.getLogger()
789 self.common_logger_config(root, config, incremental)
790
791dictConfigClass = DictConfigurator
792
793def dictConfig(config):
794 """Configure logging using a dictionary."""
795 dictConfigClass(config).configure()
796
797
Vinay Sajip4ded5512012-10-02 15:56:16 +0100798def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000799 """
800 Start up a socket server on the specified port, and listen for new
801 configurations.
802
803 These will be sent as a file suitable for processing by fileConfig().
804 Returns a Thread object on which you can call start() to start the server,
805 and which you can join() when appropriate. To stop the server, call
806 stopListening().
Vinay Sajip3e763da2012-10-02 16:15:33 +0100807
808 Use the ``verify`` argument to verify any bytes received across the wire
809 from a client. If specified, it should be a callable which receives a
810 single argument - the bytes of configuration data received across the
811 network - and it should return either ``None``, to indicate that the
812 passed in bytes could not be verified and should be discarded, or a
813 byte string which is then passed to the configuration machinery as
814 normal. Note that you can return transformed bytes, e.g. by decrypting
815 the bytes passed in.
Guido van Rossum57102f82002-11-13 16:15:58 +0000816 """
Vinay Sajip985ef872011-04-26 19:34:04 +0100817 if not thread: #pragma: no cover
Collin Winterce36ad82007-08-30 01:19:48 +0000818 raise NotImplementedError("listen() needs threading to work")
Guido van Rossum57102f82002-11-13 16:15:58 +0000819
820 class ConfigStreamHandler(StreamRequestHandler):
821 """
822 Handler for a logging configuration request.
823
824 It expects a completely new logging configuration and uses fileConfig
825 to install it.
826 """
827 def handle(self):
828 """
829 Handle a request.
830
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000831 Each request is expected to be a 4-byte length, packed using
832 struct.pack(">L", n), followed by the config file.
833 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000834 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000835 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 Sajip4ded5512012-10-02 15:56:16 +0100843 if self.server.verify is not None:
844 chunk = self.server.verify(chunk)
845 if chunk is not None: # verified, can process
846 chunk = chunk.decode("utf-8")
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000847 try:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100848 import json
849 d =json.loads(chunk)
850 assert isinstance(d, dict)
851 dictConfig(d)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100852 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100853 #Apply new configuration.
854
855 file = io.StringIO(chunk)
856 try:
857 fileConfig(file)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100858 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100859 traceback.print_exc()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000860 if self.server.ready:
861 self.server.ready.set()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200862 except OSError as e:
Vinay Sajip71dcb282014-03-20 13:03:17 +0000863 if e.errno != RESET_ERROR:
Guido van Rossum57102f82002-11-13 16:15:58 +0000864 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000865
866 class ConfigSocketReceiver(ThreadingTCPServer):
867 """
868 A simple TCP socket-based logging config receiver.
869 """
870
871 allow_reuse_address = 1
872
873 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100874 handler=None, ready=None, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000875 ThreadingTCPServer.__init__(self, (host, port), handler)
876 logging._acquireLock()
877 self.abort = 0
878 logging._releaseLock()
879 self.timeout = 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000880 self.ready = ready
Vinay Sajip4ded5512012-10-02 15:56:16 +0100881 self.verify = verify
Guido van Rossum57102f82002-11-13 16:15:58 +0000882
883 def serve_until_stopped(self):
884 import select
885 abort = 0
886 while not abort:
887 rd, wr, ex = select.select([self.socket.fileno()],
888 [], [],
889 self.timeout)
890 if rd:
891 self.handle_request()
892 logging._acquireLock()
893 abort = self.abort
894 logging._releaseLock()
Brian Curtin6ff2a7d2010-10-31 04:40:53 +0000895 self.socket.close()
Guido van Rossum57102f82002-11-13 16:15:58 +0000896
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000897 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000898
Vinay Sajip4ded5512012-10-02 15:56:16 +0100899 def __init__(self, rcvr, hdlr, port, verify):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000900 super(Server, self).__init__()
901 self.rcvr = rcvr
902 self.hdlr = hdlr
903 self.port = port
Vinay Sajip4ded5512012-10-02 15:56:16 +0100904 self.verify = verify
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000905 self.ready = threading.Event()
906
907 def run(self):
908 server = self.rcvr(port=self.port, handler=self.hdlr,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100909 ready=self.ready,
910 verify=self.verify)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000911 if self.port == 0:
912 self.port = server.server_address[1]
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000913 self.ready.set()
914 global _listener
915 logging._acquireLock()
916 _listener = server
917 logging._releaseLock()
918 server.serve_until_stopped()
919
Vinay Sajip4ded5512012-10-02 15:56:16 +0100920 return Server(ConfigSocketReceiver, ConfigStreamHandler, port, verify)
Guido van Rossum57102f82002-11-13 16:15:58 +0000921
922def stopListening():
923 """
924 Stop the listening server which was created with a call to listen().
925 """
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000926 global _listener
Vinay Sajip9fdd11b2010-09-25 17:48:25 +0000927 logging._acquireLock()
928 try:
929 if _listener:
930 _listener.abort = 1
931 _listener = None
932 finally:
Guido van Rossum57102f82002-11-13 16:15:58 +0000933 logging._releaseLock()