]> git.lizzy.rs Git - plan9front.git/blob - sys/lib/python/logging/config.py
/sys/lib/dist/ndb/common: correct authdom=inri
[plan9front.git] / sys / lib / python / logging / config.py
1 # Copyright 2001-2005 by Vinay Sajip. All Rights Reserved.
2 #
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.
16
17 """
18 Configuration functions for the logging package for Python. The core package
19 is based on PEP 282 and comments thereto in comp.lang.python, and influenced
20 by Apache's log4j system.
21
22 Should work under Python versions >= 1.5.2, except that source line
23 information is not available unless 'sys._getframe()' is.
24
25 Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved.
26
27 To use, simply 'import logging' and log away!
28 """
29
30 import sys, logging, logging.handlers, string, socket, struct, os, traceback, types
31
32 try:
33     import thread
34     import threading
35 except ImportError:
36     thread = None
37
38 from SocketServer import ThreadingTCPServer, StreamRequestHandler
39
40
41 DEFAULT_LOGGING_CONFIG_PORT = 9030
42
43 if sys.platform == "win32":
44     RESET_ERROR = 10054   #WSAECONNRESET
45 else:
46     RESET_ERROR = 104     #ECONNRESET
47
48 #
49 #   The following code implements a socket listener for on-the-fly
50 #   reconfiguration of logging.
51 #
52 #   _listener holds the server object doing the listening
53 _listener = None
54
55 def fileConfig(fname, defaults=None):
56     """
57     Read the logging configuration from a ConfigParser-format file.
58
59     This can be called several times from an application, allowing an end user
60     the ability to select from various pre-canned configurations (if the
61     developer provides a mechanism to present the choices and load the chosen
62     configuration).
63     In versions of ConfigParser which have the readfp method [typically
64     shipped in 2.x versions of Python], you can pass in a file-like object
65     rather than a filename, in which case the file-like object will be read
66     using readfp.
67     """
68     import ConfigParser
69
70     cp = ConfigParser.ConfigParser(defaults)
71     if hasattr(cp, 'readfp') and hasattr(fname, 'readline'):
72         cp.readfp(fname)
73     else:
74         cp.read(fname)
75
76     formatters = _create_formatters(cp)
77
78     # critical section
79     logging._acquireLock()
80     try:
81         logging._handlers.clear()
82         del logging._handlerList[:]
83         # Handlers add themselves to logging._handlers
84         handlers = _install_handlers(cp, formatters)
85         _install_loggers(cp, handlers)
86     finally:
87         logging._releaseLock()
88
89
90 def _resolve(name):
91     """Resolve a dotted name to a global object."""
92     name = string.split(name, '.')
93     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
104
105 def _create_formatters(cp):
106     """Create and return formatters"""
107     flist = cp.get("formatters", "keys")
108     if not len(flist):
109         return {}
110     flist = string.split(flist, ",")
111     formatters = {}
112     for form in flist:
113         sectname = "formatter_%s" % string.strip(form)
114         opts = cp.options(sectname)
115         if "format" in opts:
116             fs = cp.get(sectname, "format", 1)
117         else:
118             fs = None
119         if "datefmt" in opts:
120             dfs = cp.get(sectname, "datefmt", 1)
121         else:
122             dfs = None
123         c = logging.Formatter
124         if "class" in opts:
125             class_name = cp.get(sectname, "class")
126             if class_name:
127                 c = _resolve(class_name)
128         f = c(fs, dfs)
129         formatters[form] = f
130     return formatters
131
132
133 def _install_handlers(cp, formatters):
134     """Install and return handlers"""
135     hlist = cp.get("handlers", "keys")
136     if not len(hlist):
137         return {}
138     hlist = string.split(hlist, ",")
139     handlers = {}
140     fixups = [] #for inter-handler references
141     for hand in hlist:
142         sectname = "handler_%s" % string.strip(hand)
143         klass = cp.get(sectname, "class")
144         opts = cp.options(sectname)
145         if "formatter" in opts:
146             fmt = cp.get(sectname, "formatter")
147         else:
148             fmt = ""
149         klass = eval(klass, vars(logging))
150         args = cp.get(sectname, "args")
151         args = eval(args, vars(logging))
152         h = apply(klass, args)
153         if "level" in opts:
154             level = cp.get(sectname, "level")
155             h.setLevel(logging._levelNames[level])
156         if len(fmt):
157             h.setFormatter(formatters[fmt])
158         #temporary hack for FileHandler and MemoryHandler.
159         if klass == logging.handlers.MemoryHandler:
160             if "target" in opts:
161                 target = cp.get(sectname,"target")
162             else:
163                 target = ""
164             if len(target): #the target handler may not be loaded yet, so keep for later...
165                 fixups.append((h, target))
166         handlers[hand] = h
167     #now all handlers are loaded, fixup inter-handler references...
168     for h, t in fixups:
169         h.setTarget(handlers[t])
170     return handlers
171
172
173 def _install_loggers(cp, handlers):
174     """Create and install loggers"""
175
176     # configure the root first
177     llist = cp.get("loggers", "keys")
178     llist = string.split(llist, ",")
179     llist = map(lambda x: string.strip(x), llist)
180     llist.remove("root")
181     sectname = "logger_root"
182     root = logging.root
183     log = root
184     opts = cp.options(sectname)
185     if "level" in opts:
186         level = cp.get(sectname, "level")
187         log.setLevel(logging._levelNames[level])
188     for h in root.handlers[:]:
189         root.removeHandler(h)
190     hlist = cp.get(sectname, "handlers")
191     if len(hlist):
192         hlist = string.split(hlist, ",")
193         for hand in hlist:
194             log.addHandler(handlers[string.strip(hand)])
195
196     #and now the others...
197     #we don't want to lose the existing loggers,
198     #since other threads may have pointers to them.
199     #existing is set to contain all existing loggers,
200     #and as we go through the new configuration we
201     #remove any which are configured. At the end,
202     #what's left in existing is the set of loggers
203     #which were in the previous configuration but
204     #which are not in the new configuration.
205     existing = root.manager.loggerDict.keys()
206     #now set up the new ones...
207     for log in llist:
208         sectname = "logger_%s" % log
209         qn = cp.get(sectname, "qualname")
210         opts = cp.options(sectname)
211         if "propagate" in opts:
212             propagate = cp.getint(sectname, "propagate")
213         else:
214             propagate = 1
215         logger = logging.getLogger(qn)
216         if qn in existing:
217             existing.remove(qn)
218         if "level" in opts:
219             level = cp.get(sectname, "level")
220             logger.setLevel(logging._levelNames[level])
221         for h in logger.handlers[:]:
222             logger.removeHandler(h)
223         logger.propagate = propagate
224         logger.disabled = 0
225         hlist = cp.get(sectname, "handlers")
226         if len(hlist):
227             hlist = string.split(hlist, ",")
228             for hand in hlist:
229                 logger.addHandler(handlers[string.strip(hand)])
230
231     #Disable any old loggers. There's no point deleting
232     #them as other threads may continue to hold references
233     #and by disabling them, you stop them doing any logging.
234     for log in existing:
235         root.manager.loggerDict[log].disabled = 1
236
237
238 def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
239     """
240     Start up a socket server on the specified port, and listen for new
241     configurations.
242
243     These will be sent as a file suitable for processing by fileConfig().
244     Returns a Thread object on which you can call start() to start the server,
245     and which you can join() when appropriate. To stop the server, call
246     stopListening().
247     """
248     if not thread:
249         raise NotImplementedError, "listen() needs threading to work"
250
251     class ConfigStreamHandler(StreamRequestHandler):
252         """
253         Handler for a logging configuration request.
254
255         It expects a completely new logging configuration and uses fileConfig
256         to install it.
257         """
258         def handle(self):
259             """
260             Handle a request.
261
262             Each request is expected to be a 4-byte length, packed using
263             struct.pack(">L", n), followed by the config file.
264             Uses fileConfig() to do the grunt work.
265             """
266             import tempfile
267             try:
268                 conn = self.connection
269                 chunk = conn.recv(4)
270                 if len(chunk) == 4:
271                     slen = struct.unpack(">L", chunk)[0]
272                     chunk = self.connection.recv(slen)
273                     while len(chunk) < slen:
274                         chunk = chunk + conn.recv(slen - len(chunk))
275                     #Apply new configuration. We'd like to be able to
276                     #create a StringIO and pass that in, but unfortunately
277                     #1.5.2 ConfigParser does not support reading file
278                     #objects, only actual files. So we create a temporary
279                     #file and remove it later.
280                     file = tempfile.mktemp(".ini")
281                     f = open(file, "w")
282                     f.write(chunk)
283                     f.close()
284                     try:
285                         fileConfig(file)
286                     except (KeyboardInterrupt, SystemExit):
287                         raise
288                     except:
289                         traceback.print_exc()
290                     os.remove(file)
291             except socket.error, e:
292                 if type(e.args) != types.TupleType:
293                     raise
294                 else:
295                     errcode = e.args[0]
296                     if errcode != RESET_ERROR:
297                         raise
298
299     class ConfigSocketReceiver(ThreadingTCPServer):
300         """
301         A simple TCP socket-based logging config receiver.
302         """
303
304         allow_reuse_address = 1
305
306         def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
307                      handler=None):
308             ThreadingTCPServer.__init__(self, (host, port), handler)
309             logging._acquireLock()
310             self.abort = 0
311             logging._releaseLock()
312             self.timeout = 1
313
314         def serve_until_stopped(self):
315             import select
316             abort = 0
317             while not abort:
318                 rd, wr, ex = select.select([self.socket.fileno()],
319                                            [], [],
320                                            self.timeout)
321                 if rd:
322                     self.handle_request()
323                 logging._acquireLock()
324                 abort = self.abort
325                 logging._releaseLock()
326
327     def serve(rcvr, hdlr, port):
328         server = rcvr(port=port, handler=hdlr)
329         global _listener
330         logging._acquireLock()
331         _listener = server
332         logging._releaseLock()
333         server.serve_until_stopped()
334
335     return threading.Thread(target=serve,
336                             args=(ConfigSocketReceiver,
337                                   ConfigStreamHandler, port))
338
339 def stopListening():
340     """
341     Stop the listening server which was created with a call to listen().
342     """
343     global _listener
344     if _listener:
345         logging._acquireLock()
346         _listener.abort = 1
347         _listener = None
348         logging._releaseLock()