blob: 895fb263c0287466e056793cde301f9a2e84cab4 [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 Sajip7a7160b2006-01-20 18:28:03 +0000119 c = logging.Formatter
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000120 class_name = cp[sectname].get("class")
121 if class_name:
122 c = _resolve(class_name)
Vinay Sajip7a7160b2006-01-20 18:28:03 +0000123 f = c(fs, dfs)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000124 formatters[form] = f
125 return formatters
126
127
128def _install_handlers(cp, formatters):
129 """Install and return handlers"""
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000130 hlist = cp["handlers"]["keys"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000131 if not len(hlist):
132 return {}
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000133 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000134 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000135 handlers = {}
136 fixups = [] #for inter-handler references
137 for hand in hlist:
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000138 section = cp["handler_%s" % hand]
139 klass = section["class"]
140 fmt = section.get("formatter", "")
Georg Brandl3dbca812008-07-23 16:10:53 +0000141 try:
142 klass = eval(klass, vars(logging))
143 except (AttributeError, NameError):
144 klass = _resolve(klass)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000145 args = section["args"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000146 args = eval(args, vars(logging))
Neal Norwitzd9108552006-03-17 08:00:19 +0000147 h = klass(*args)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000148 if "level" in section:
149 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700150 h.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000151 if len(fmt):
152 h.setFormatter(formatters[fmt])
Benjamin Peterson41181742008-07-02 20:22:54 +0000153 if issubclass(klass, logging.handlers.MemoryHandler):
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000154 target = section.get("target", "")
Vinay Sajip989b69a2006-01-16 21:28:37 +0000155 if len(target): #the target handler may not be loaded yet, so keep for later...
156 fixups.append((h, target))
157 handlers[hand] = h
158 #now all handlers are loaded, fixup inter-handler references...
159 for h, t in fixups:
160 h.setTarget(handlers[t])
161 return handlers
162
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000163def _handle_existing_loggers(existing, child_loggers, disable_existing):
164 """
165 When (re)configuring logging, handle loggers which were in the previous
166 configuration but are not in the new configuration. There's no point
167 deleting them as other threads may continue to hold references to them;
168 and by disabling them, you stop them doing any logging.
Vinay Sajip989b69a2006-01-16 21:28:37 +0000169
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000170 However, don't disable children of named loggers, as that's probably not
171 what was intended by the user. Also, allow existing loggers to NOT be
172 disabled if disable_existing is false.
173 """
174 root = logging.root
175 for log in existing:
176 logger = root.manager.loggerDict[log]
177 if log in child_loggers:
178 logger.level = logging.NOTSET
179 logger.handlers = []
180 logger.propagate = True
Vinay Sajip68b4cc82013-03-23 11:18:45 +0000181 else:
182 logger.disabled = disable_existing
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000183
184def _install_loggers(cp, handlers, disable_existing):
Vinay Sajip989b69a2006-01-16 21:28:37 +0000185 """Create and install loggers"""
186
187 # configure the root first
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000188 llist = cp["loggers"]["keys"]
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000189 llist = llist.split(",")
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000190 llist = list(map(lambda x: x.strip(), llist))
Vinay Sajip989b69a2006-01-16 21:28:37 +0000191 llist.remove("root")
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000192 section = cp["logger_root"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000193 root = logging.root
194 log = root
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000195 if "level" in section:
196 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700197 log.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000198 for h in root.handlers[:]:
199 root.removeHandler(h)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000200 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000201 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000202 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000203 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000204 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +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.
Guido van Rossum8b8a5432007-02-12 00:07:01 +0000216 existing = list(root.manager.loggerDict.keys())
Christian Heimes96f31632007-11-12 01:32:03 +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.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100221 existing.sort()
Christian Heimes96f31632007-11-12 01:32:03 +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:
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000227 section = cp["logger_%s" % log]
228 qn = section["qualname"]
229 propagate = section.getint("propagate", fallback=1)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000230 logger = logging.getLogger(qn)
231 if qn in existing:
Vinay Sajip3f84b072011-03-07 17:49:33 +0000232 i = existing.index(qn) + 1 # start with the entry after qn
Christian Heimes96f31632007-11-12 01:32:03 +0000233 prefixed = qn + "."
234 pflen = len(prefixed)
235 num_existing = len(existing)
Vinay Sajip3f84b072011-03-07 17:49:33 +0000236 while i < num_existing:
237 if existing[i][:pflen] == prefixed:
238 child_loggers.append(existing[i])
239 i += 1
Vinay Sajip989b69a2006-01-16 21:28:37 +0000240 existing.remove(qn)
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000241 if "level" in section:
242 level = section["level"]
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700243 logger.setLevel(level)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000244 for h in logger.handlers[:]:
245 logger.removeHandler(h)
246 logger.propagate = propagate
247 logger.disabled = 0
Ɓukasz Langa26d513c2010-11-10 18:57:39 +0000248 hlist = section["handlers"]
Vinay Sajip989b69a2006-01-16 21:28:37 +0000249 if len(hlist):
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000250 hlist = hlist.split(",")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000251 hlist = _strip_spaces(hlist)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000252 for hand in hlist:
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000253 logger.addHandler(handlers[hand])
Vinay Sajip989b69a2006-01-16 21:28:37 +0000254
255 #Disable any old loggers. There's no point deleting
256 #them as other threads may continue to hold references
257 #and by disabling them, you stop them doing any logging.
Christian Heimes96f31632007-11-12 01:32:03 +0000258 #However, don't disable children of named loggers, as that's
259 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000260 #for log in existing:
261 # logger = root.manager.loggerDict[log]
262 # if log in child_loggers:
263 # logger.level = logging.NOTSET
264 # logger.handlers = []
265 # logger.propagate = 1
266 # elif disable_existing_loggers:
267 # logger.disabled = 1
268 _handle_existing_loggers(existing, child_loggers, disable_existing)
Vinay Sajip989b69a2006-01-16 21:28:37 +0000269
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000270IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
271
272
273def valid_ident(s):
274 m = IDENTIFIER.match(s)
275 if not m:
276 raise ValueError('Not a valid Python identifier: %r' % s)
277 return True
278
279
Vinay Sajipb1698d42014-03-20 13:14:39 +0000280class ConvertingMixin(object):
281 """For ConvertingXXX's, this mixin class provides common functions"""
282
283 def convert_with_key(self, key, value, replace=True):
284 result = self.configurator.convert(value)
285 #If the converted value is different, save for next time
286 if value is not result:
287 if replace:
288 self[key] = result
289 if type(result) in (ConvertingDict, ConvertingList,
290 ConvertingTuple):
291 result.parent = self
292 result.key = key
293 return result
294
295 def convert(self, value):
296 result = self.configurator.convert(value)
297 if value is not result:
298 if type(result) in (ConvertingDict, ConvertingList,
299 ConvertingTuple):
300 result.parent = self
301 return result
302
303
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000304# The ConvertingXXX classes are wrappers around standard Python containers,
305# and they serve to convert any suitable values in the container. The
306# conversion converts base dicts, lists and tuples to their wrapped
307# equivalents, whereas strings which match a conversion format are converted
308# appropriately.
309#
310# Each wrapper should have a configurator attribute holding the actual
311# configurator to use for conversion.
312
Vinay Sajipb1698d42014-03-20 13:14:39 +0000313class ConvertingDict(dict, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000314 """A converting dictionary wrapper."""
315
316 def __getitem__(self, key):
317 value = dict.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000318 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000319
320 def get(self, key, default=None):
321 value = dict.get(self, key, default)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000322 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000323
324 def pop(self, key, default=None):
325 value = dict.pop(self, key, default)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000326 return self.convert_with_key(key, value, replace=False)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000327
Vinay Sajipb1698d42014-03-20 13:14:39 +0000328class ConvertingList(list, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000329 """A converting list wrapper."""
330 def __getitem__(self, key):
331 value = list.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000332 return self.convert_with_key(key, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000333
334 def pop(self, idx=-1):
335 value = list.pop(self, idx)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000336 return self.convert(value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000337
Vinay Sajipb1698d42014-03-20 13:14:39 +0000338class ConvertingTuple(tuple, ConvertingMixin):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000339 """A converting tuple wrapper."""
340 def __getitem__(self, key):
341 value = tuple.__getitem__(self, key)
Vinay Sajipb1698d42014-03-20 13:14:39 +0000342 # Can't replace a tuple entry.
343 return self.convert_with_key(key, value, replace=False)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000344
345class BaseConfigurator(object):
346 """
347 The configurator base class which defines some useful defaults.
348 """
349
350 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
351
352 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
353 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
354 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
355 DIGIT_PATTERN = re.compile(r'^\d+$')
356
357 value_converters = {
358 'ext' : 'ext_convert',
359 'cfg' : 'cfg_convert',
360 }
361
362 # We might want to use a different one, e.g. importlib
Brett Cannonc2368502010-06-12 00:39:28 +0000363 importer = staticmethod(__import__)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000364
365 def __init__(self, config):
366 self.config = ConvertingDict(config)
367 self.config.configurator = self
368
369 def resolve(self, s):
370 """
371 Resolve strings to objects using standard import and attribute
372 syntax.
373 """
374 name = s.split('.')
375 used = name.pop(0)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000376 try:
377 found = self.importer(used)
378 for frag in name:
379 used += '.' + frag
380 try:
381 found = getattr(found, frag)
382 except AttributeError:
383 self.importer(used)
384 found = getattr(found, frag)
385 return found
386 except ImportError:
387 e, tb = sys.exc_info()[1:]
388 v = ValueError('Cannot resolve %r: %s' % (s, e))
389 v.__cause__, v.__traceback__ = e, tb
390 raise v
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000391
392 def ext_convert(self, value):
393 """Default converter for the ext:// protocol."""
394 return self.resolve(value)
395
396 def cfg_convert(self, value):
397 """Default converter for the cfg:// protocol."""
398 rest = value
399 m = self.WORD_PATTERN.match(rest)
400 if m is None:
401 raise ValueError("Unable to convert %r" % value)
402 else:
403 rest = rest[m.end():]
404 d = self.config[m.groups()[0]]
405 #print d, rest
406 while rest:
407 m = self.DOT_PATTERN.match(rest)
408 if m:
409 d = d[m.groups()[0]]
410 else:
411 m = self.INDEX_PATTERN.match(rest)
412 if m:
413 idx = m.groups()[0]
414 if not self.DIGIT_PATTERN.match(idx):
415 d = d[idx]
416 else:
417 try:
418 n = int(idx) # try as number first (most likely)
419 d = d[n]
420 except TypeError:
421 d = d[idx]
422 if m:
423 rest = rest[m.end():]
424 else:
425 raise ValueError('Unable to convert '
426 '%r at %r' % (value, rest))
427 #rest should be empty
428 return d
429
430 def convert(self, value):
431 """
432 Convert values to an appropriate type. dicts, lists and tuples are
433 replaced by their converting alternatives. Strings are checked to
434 see if they have a conversion format and are converted if they do.
435 """
436 if not isinstance(value, ConvertingDict) and isinstance(value, dict):
437 value = ConvertingDict(value)
438 value.configurator = self
439 elif not isinstance(value, ConvertingList) and isinstance(value, list):
440 value = ConvertingList(value)
441 value.configurator = self
442 elif not isinstance(value, ConvertingTuple) and\
443 isinstance(value, tuple):
444 value = ConvertingTuple(value)
445 value.configurator = self
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000446 elif isinstance(value, str): # str for py3k
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000447 m = self.CONVERT_PATTERN.match(value)
448 if m:
449 d = m.groupdict()
450 prefix = d['prefix']
451 converter = self.value_converters.get(prefix, None)
452 if converter:
453 suffix = d['suffix']
454 converter = getattr(self, converter)
455 value = converter(suffix)
456 return value
457
458 def configure_custom(self, config):
459 """Configure an object with a user-supplied factory."""
460 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200461 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000462 c = self.resolve(c)
463 props = config.pop('.', None)
464 # Check for valid identifiers
465 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
466 result = c(**kwargs)
467 if props:
468 for name, value in props.items():
469 setattr(result, name, value)
470 return result
471
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000472 def as_tuple(self, value):
473 """Utility function which converts lists to tuples."""
474 if isinstance(value, list):
475 value = tuple(value)
476 return value
477
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000478class DictConfigurator(BaseConfigurator):
479 """
480 Configure logging using a dictionary-like object to describe the
481 configuration.
482 """
483
484 def configure(self):
485 """Do the configuration."""
486
487 config = self.config
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000488 if 'version' not in config:
489 raise ValueError("dictionary doesn't specify a version")
490 if config['version'] != 1:
491 raise ValueError("Unsupported version: %s" % config['version'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000492 incremental = config.pop('incremental', False)
493 EMPTY_DICT = {}
494 logging._acquireLock()
495 try:
496 if incremental:
497 handlers = config.get('handlers', EMPTY_DICT)
498 for name in handlers:
499 if name not in logging._handlers:
500 raise ValueError('No handler found with '
501 'name %r' % name)
502 else:
503 try:
504 handler = logging._handlers[name]
505 handler_config = handlers[name]
506 level = handler_config.get('level', None)
507 if level:
508 handler.setLevel(logging._checkLevel(level))
509 except Exception as e:
510 raise ValueError('Unable to configure handler '
511 '%r: %s' % (name, e))
512 loggers = config.get('loggers', EMPTY_DICT)
513 for name in loggers:
514 try:
515 self.configure_logger(name, loggers[name], True)
516 except Exception as e:
517 raise ValueError('Unable to configure logger '
518 '%r: %s' % (name, e))
519 root = config.get('root', None)
520 if root:
521 try:
522 self.configure_root(root, True)
523 except Exception as e:
524 raise ValueError('Unable to configure root '
525 'logger: %s' % e)
526 else:
527 disable_existing = config.pop('disable_existing_loggers', True)
528
529 logging._handlers.clear()
530 del logging._handlerList[:]
531
532 # Do formatters first - they don't refer to anything else
533 formatters = config.get('formatters', EMPTY_DICT)
534 for name in formatters:
535 try:
536 formatters[name] = self.configure_formatter(
537 formatters[name])
538 except Exception as e:
539 raise ValueError('Unable to configure '
540 'formatter %r: %s' % (name, e))
541 # Next, do filters - they don't refer to anything else, either
542 filters = config.get('filters', EMPTY_DICT)
543 for name in filters:
544 try:
545 filters[name] = self.configure_filter(filters[name])
546 except Exception as e:
547 raise ValueError('Unable to configure '
548 'filter %r: %s' % (name, e))
549
550 # Next, do handlers - they refer to formatters and filters
551 # As handlers can refer to other handlers, sort the keys
552 # to allow a deterministic order of configuration
553 handlers = config.get('handlers', EMPTY_DICT)
Vinay Sajip3f885b52013-03-22 15:19:54 +0000554 deferred = []
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000555 for name in sorted(handlers):
556 try:
557 handler = self.configure_handler(handlers[name])
558 handler.name = name
559 handlers[name] = handler
560 except Exception as e:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000561 if 'target not configured yet' in str(e):
562 deferred.append(name)
563 else:
564 raise ValueError('Unable to configure handler '
565 '%r: %s' % (name, e))
566
567 # Now do any that were deferred
568 for name in deferred:
569 try:
570 handler = self.configure_handler(handlers[name])
571 handler.name = name
572 handlers[name] = handler
573 except Exception as e:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000574 raise ValueError('Unable to configure handler '
575 '%r: %s' % (name, e))
Vinay Sajip3f885b52013-03-22 15:19:54 +0000576
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000577 # Next, do loggers - they refer to handlers and filters
578
579 #we don't want to lose the existing loggers,
580 #since other threads may have pointers to them.
581 #existing is set to contain all existing loggers,
582 #and as we go through the new configuration we
583 #remove any which are configured. At the end,
584 #what's left in existing is the set of loggers
585 #which were in the previous configuration but
586 #which are not in the new configuration.
587 root = logging.root
588 existing = list(root.manager.loggerDict.keys())
589 #The list needs to be sorted so that we can
590 #avoid disabling child loggers of explicitly
591 #named loggers. With a sorted list it is easier
592 #to find the child loggers.
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100593 existing.sort()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000594 #We'll keep the list of existing loggers
595 #which are children of named loggers here...
596 child_loggers = []
597 #now set up the new ones...
598 loggers = config.get('loggers', EMPTY_DICT)
599 for name in loggers:
600 if name in existing:
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000601 i = existing.index(name) + 1 # look after name
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000602 prefixed = name + "."
603 pflen = len(prefixed)
604 num_existing = len(existing)
Vinay Sajip9f9991c2011-03-07 18:02:57 +0000605 while i < num_existing:
606 if existing[i][:pflen] == prefixed:
607 child_loggers.append(existing[i])
608 i += 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000609 existing.remove(name)
610 try:
611 self.configure_logger(name, loggers[name])
612 except Exception as e:
613 raise ValueError('Unable to configure logger '
614 '%r: %s' % (name, e))
615
616 #Disable any old loggers. There's no point deleting
617 #them as other threads may continue to hold references
618 #and by disabling them, you stop them doing any logging.
619 #However, don't disable children of named loggers, as that's
620 #probably not what was intended by the user.
Vinay Sajipec1cd1c2010-08-30 19:02:14 +0000621 #for log in existing:
622 # logger = root.manager.loggerDict[log]
623 # if log in child_loggers:
624 # logger.level = logging.NOTSET
625 # logger.handlers = []
626 # logger.propagate = True
627 # elif disable_existing:
628 # logger.disabled = True
629 _handle_existing_loggers(existing, child_loggers,
630 disable_existing)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000631
632 # And finally, do the root logger
633 root = config.get('root', None)
634 if root:
635 try:
636 self.configure_root(root)
637 except Exception as e:
638 raise ValueError('Unable to configure root '
639 'logger: %s' % e)
640 finally:
641 logging._releaseLock()
642
643 def configure_formatter(self, config):
644 """Configure a formatter from a dictionary."""
645 if '()' in config:
646 factory = config['()'] # for use in exception handler
647 try:
648 result = self.configure_custom(config)
649 except TypeError as te:
650 if "'format'" not in str(te):
651 raise
652 #Name of parameter changed from fmt to format.
653 #Retry with old name.
654 #This is so that code can be used with older Python versions
655 #(e.g. by Django)
656 config['fmt'] = config.pop('format')
657 config['()'] = factory
658 result = self.configure_custom(config)
659 else:
660 fmt = config.get('format', None)
661 dfmt = config.get('datefmt', None)
Vinay Sajip28421c62013-03-29 17:56:54 +0000662 style = config.get('style', '%')
663 result = logging.Formatter(fmt, dfmt, style)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000664 return result
665
666 def configure_filter(self, config):
667 """Configure a filter from a dictionary."""
668 if '()' in config:
669 result = self.configure_custom(config)
670 else:
671 name = config.get('name', '')
672 result = logging.Filter(name)
673 return result
674
675 def add_filters(self, filterer, filters):
676 """Add filters to a filterer from a list of names."""
677 for f in filters:
678 try:
679 filterer.addFilter(self.config['filters'][f])
680 except Exception as e:
681 raise ValueError('Unable to add filter %r: %s' % (f, e))
682
683 def configure_handler(self, config):
684 """Configure a handler from a dictionary."""
Vinay Sajip28421c62013-03-29 17:56:54 +0000685 config_copy = dict(config) # for restoring in case of error
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000686 formatter = config.pop('formatter', None)
687 if formatter:
688 try:
689 formatter = self.config['formatters'][formatter]
690 except Exception as e:
691 raise ValueError('Unable to set formatter '
692 '%r: %s' % (formatter, e))
693 level = config.pop('level', None)
694 filters = config.pop('filters', None)
695 if '()' in config:
696 c = config.pop('()')
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200697 if not callable(c):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000698 c = self.resolve(c)
699 factory = c
700 else:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000701 cname = config.pop('class')
702 klass = self.resolve(cname)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000703 #Special case for handler which refers to another handler
704 if issubclass(klass, logging.handlers.MemoryHandler) and\
705 'target' in config:
706 try:
Vinay Sajip3f885b52013-03-22 15:19:54 +0000707 th = self.config['handlers'][config['target']]
708 if not isinstance(th, logging.Handler):
Vinay Sajip28421c62013-03-29 17:56:54 +0000709 config.update(config_copy) # restore for deferred cfg
Vinay Sajip3f885b52013-03-22 15:19:54 +0000710 raise TypeError('target not configured yet')
711 config['target'] = th
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000712 except Exception as e:
713 raise ValueError('Unable to set target handler '
714 '%r: %s' % (config['target'], e))
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000715 elif issubclass(klass, logging.handlers.SMTPHandler) and\
716 'mailhost' in config:
717 config['mailhost'] = self.as_tuple(config['mailhost'])
718 elif issubclass(klass, logging.handlers.SysLogHandler) and\
719 'address' in config:
720 config['address'] = self.as_tuple(config['address'])
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000721 factory = klass
Vinay Sajip8d270232012-11-15 14:20:18 +0000722 props = config.pop('.', None)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000723 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
724 try:
725 result = factory(**kwargs)
726 except TypeError as te:
727 if "'stream'" not in str(te):
728 raise
729 #The argument name changed from strm to stream
730 #Retry with old name.
731 #This is so that code can be used with older Python versions
732 #(e.g. by Django)
733 kwargs['strm'] = kwargs.pop('stream')
734 result = factory(**kwargs)
735 if formatter:
736 result.setFormatter(formatter)
737 if level is not None:
738 result.setLevel(logging._checkLevel(level))
739 if filters:
740 self.add_filters(result, filters)
Vinay Sajip8d270232012-11-15 14:20:18 +0000741 if props:
742 for name, value in props.items():
743 setattr(result, name, value)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000744 return result
745
746 def add_handlers(self, logger, handlers):
747 """Add handlers to a logger from a list of names."""
748 for h in handlers:
749 try:
750 logger.addHandler(self.config['handlers'][h])
751 except Exception as e:
752 raise ValueError('Unable to add handler %r: %s' % (h, e))
753
754 def common_logger_config(self, logger, config, incremental=False):
755 """
756 Perform configuration which is common to root and non-root loggers.
757 """
758 level = config.get('level', None)
759 if level is not None:
760 logger.setLevel(logging._checkLevel(level))
761 if not incremental:
762 #Remove any existing handlers
763 for h in logger.handlers[:]:
764 logger.removeHandler(h)
765 handlers = config.get('handlers', None)
766 if handlers:
767 self.add_handlers(logger, handlers)
768 filters = config.get('filters', None)
769 if filters:
770 self.add_filters(logger, filters)
771
772 def configure_logger(self, name, config, incremental=False):
773 """Configure a non-root logger from a dictionary."""
774 logger = logging.getLogger(name)
775 self.common_logger_config(logger, config, incremental)
776 propagate = config.get('propagate', None)
777 if propagate is not None:
778 logger.propagate = propagate
779
780 def configure_root(self, config, incremental=False):
781 """Configure a root logger from a dictionary."""
782 root = logging.getLogger()
783 self.common_logger_config(root, config, incremental)
784
785dictConfigClass = DictConfigurator
786
787def dictConfig(config):
788 """Configure logging using a dictionary."""
789 dictConfigClass(config).configure()
790
791
Vinay Sajip4ded5512012-10-02 15:56:16 +0100792def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000793 """
794 Start up a socket server on the specified port, and listen for new
795 configurations.
796
797 These will be sent as a file suitable for processing by fileConfig().
798 Returns a Thread object on which you can call start() to start the server,
799 and which you can join() when appropriate. To stop the server, call
800 stopListening().
Vinay Sajip3e763da2012-10-02 16:15:33 +0100801
802 Use the ``verify`` argument to verify any bytes received across the wire
803 from a client. If specified, it should be a callable which receives a
804 single argument - the bytes of configuration data received across the
805 network - and it should return either ``None``, to indicate that the
806 passed in bytes could not be verified and should be discarded, or a
807 byte string which is then passed to the configuration machinery as
808 normal. Note that you can return transformed bytes, e.g. by decrypting
809 the bytes passed in.
Guido van Rossum57102f82002-11-13 16:15:58 +0000810 """
Vinay Sajip985ef872011-04-26 19:34:04 +0100811 if not thread: #pragma: no cover
Collin Winterce36ad82007-08-30 01:19:48 +0000812 raise NotImplementedError("listen() needs threading to work")
Guido van Rossum57102f82002-11-13 16:15:58 +0000813
814 class ConfigStreamHandler(StreamRequestHandler):
815 """
816 Handler for a logging configuration request.
817
818 It expects a completely new logging configuration and uses fileConfig
819 to install it.
820 """
821 def handle(self):
822 """
823 Handle a request.
824
Vinay Sajip4c1423b2005-06-05 20:39:36 +0000825 Each request is expected to be a 4-byte length, packed using
826 struct.pack(">L", n), followed by the config file.
827 Uses fileConfig() to do the grunt work.
Guido van Rossum57102f82002-11-13 16:15:58 +0000828 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000829 try:
830 conn = self.connection
831 chunk = conn.recv(4)
832 if len(chunk) == 4:
833 slen = struct.unpack(">L", chunk)[0]
834 chunk = self.connection.recv(slen)
835 while len(chunk) < slen:
836 chunk = chunk + conn.recv(slen - len(chunk))
Vinay Sajip4ded5512012-10-02 15:56:16 +0100837 if self.server.verify is not None:
838 chunk = self.server.verify(chunk)
839 if chunk is not None: # verified, can process
840 chunk = chunk.decode("utf-8")
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000841 try:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100842 import json
843 d =json.loads(chunk)
844 assert isinstance(d, dict)
845 dictConfig(d)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100846 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100847 #Apply new configuration.
848
849 file = io.StringIO(chunk)
850 try:
851 fileConfig(file)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100852 except Exception:
Vinay Sajip4ded5512012-10-02 15:56:16 +0100853 traceback.print_exc()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000854 if self.server.ready:
855 self.server.ready.set()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200856 except OSError as e:
Vinay Sajip71dcb282014-03-20 13:03:17 +0000857 if e.errno != RESET_ERROR:
Guido van Rossum57102f82002-11-13 16:15:58 +0000858 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000859
860 class ConfigSocketReceiver(ThreadingTCPServer):
861 """
862 A simple TCP socket-based logging config receiver.
863 """
864
865 allow_reuse_address = 1
866
867 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100868 handler=None, ready=None, verify=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000869 ThreadingTCPServer.__init__(self, (host, port), handler)
870 logging._acquireLock()
871 self.abort = 0
872 logging._releaseLock()
873 self.timeout = 1
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000874 self.ready = ready
Vinay Sajip4ded5512012-10-02 15:56:16 +0100875 self.verify = verify
Guido van Rossum57102f82002-11-13 16:15:58 +0000876
877 def serve_until_stopped(self):
878 import select
879 abort = 0
880 while not abort:
881 rd, wr, ex = select.select([self.socket.fileno()],
882 [], [],
883 self.timeout)
884 if rd:
885 self.handle_request()
886 logging._acquireLock()
887 abort = self.abort
888 logging._releaseLock()
Brian Curtin6ff2a7d2010-10-31 04:40:53 +0000889 self.socket.close()
Guido van Rossum57102f82002-11-13 16:15:58 +0000890
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000891 class Server(threading.Thread):
Guido van Rossum57102f82002-11-13 16:15:58 +0000892
Vinay Sajip4ded5512012-10-02 15:56:16 +0100893 def __init__(self, rcvr, hdlr, port, verify):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000894 super(Server, self).__init__()
895 self.rcvr = rcvr
896 self.hdlr = hdlr
897 self.port = port
Vinay Sajip4ded5512012-10-02 15:56:16 +0100898 self.verify = verify
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000899 self.ready = threading.Event()
900
901 def run(self):
902 server = self.rcvr(port=self.port, handler=self.hdlr,
Vinay Sajip4ded5512012-10-02 15:56:16 +0100903 ready=self.ready,
904 verify=self.verify)
Benjamin Petersona82addb2010-06-27 20:54:28 +0000905 if self.port == 0:
906 self.port = server.server_address[1]
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000907 self.ready.set()
908 global _listener
909 logging._acquireLock()
910 _listener = server
911 logging._releaseLock()
912 server.serve_until_stopped()
913
Vinay Sajip4ded5512012-10-02 15:56:16 +0100914 return Server(ConfigSocketReceiver, ConfigStreamHandler, port, verify)
Guido van Rossum57102f82002-11-13 16:15:58 +0000915
916def stopListening():
917 """
918 Stop the listening server which was created with a call to listen().
919 """
Neal Norwitzc4d047a2002-11-15 23:33:20 +0000920 global _listener
Vinay Sajip9fdd11b2010-09-25 17:48:25 +0000921 logging._acquireLock()
922 try:
923 if _listener:
924 _listener.abort = 1
925 _listener = None
926 finally:
Guido van Rossum57102f82002-11-13 16:15:58 +0000927 logging._releaseLock()