| 
									
										
										
										
											2002-11-13 16:15:58 +00:00
										 |  |  | # Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Permission to use, copy, modify, and distribute this software and its | 
					
						
							|  |  |  | # documentation for any purpose and without fee is hereby granted, | 
					
						
							|  |  |  | # provided that the above copyright notice appear in all copies and that | 
					
						
							|  |  |  | # both that copyright notice and this permission notice appear in | 
					
						
							|  |  |  | # supporting documentation, and that the name of Vinay Sajip | 
					
						
							|  |  |  | # not be used in advertising or publicity pertaining to distribution | 
					
						
							|  |  |  | # of the software without specific, written prior permission. | 
					
						
							|  |  |  | # VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING | 
					
						
							|  |  |  | # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL | 
					
						
							|  |  |  | # VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR | 
					
						
							|  |  |  | # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | 
					
						
							|  |  |  | # IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | 
					
						
							|  |  |  | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | Logging package for Python. Based on PEP 282 and comments thereto in | 
					
						
							|  |  |  | comp.lang.python, and influenced by Apache's log4j system. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Should work under Python versions >= 1.5.2, except that source line | 
					
						
							|  |  |  | information is not available unless 'inspect' is. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | To use, simply 'import logging' and log away! | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import sys, logging, logging.handlers, string, thread, threading, socket, struct, os | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from SocketServer import ThreadingTCPServer, StreamRequestHandler | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | DEFAULT_LOGGING_CONFIG_PORT = 9030 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #   The following code implements a socket listener for on-the-fly | 
					
						
							|  |  |  | #   reconfiguration of logging. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #   _listener holds the server object doing the listening | 
					
						
							|  |  |  | _listener = None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-14 12:52:17 +00:00
										 |  |  | def fileConfig(fname, defaults=None): | 
					
						
							| 
									
										
										
										
											2002-11-13 16:15:58 +00:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     Read the logging configuration from a ConfigParser-format file. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This can be called several times from an application, allowing an end user | 
					
						
							|  |  |  |     the ability to select from various pre-canned configurations (if the | 
					
						
							|  |  |  |     developer provides a mechanism to present the choices and load the chosen | 
					
						
							|  |  |  |     configuration). | 
					
						
							|  |  |  |     In versions of ConfigParser which have the readfp method [typically | 
					
						
							|  |  |  |     shipped in 2.x versions of Python], you can pass in a file-like object | 
					
						
							|  |  |  |     rather than a filename, in which case the file-like object will be read | 
					
						
							|  |  |  |     using readfp. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     import ConfigParser | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-14 12:52:17 +00:00
										 |  |  |     cp = ConfigParser.ConfigParser(defaults) | 
					
						
							| 
									
										
										
										
											2002-11-13 16:15:58 +00:00
										 |  |  |     if hasattr(cp, 'readfp') and hasattr(fname, 'readline'): | 
					
						
							|  |  |  |         cp.readfp(fname) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         cp.read(fname) | 
					
						
							|  |  |  |     #first, do the formatters... | 
					
						
							|  |  |  |     flist = cp.get("formatters", "keys") | 
					
						
							|  |  |  |     if len(flist): | 
					
						
							|  |  |  |         flist = string.split(flist, ",") | 
					
						
							|  |  |  |         formatters = {} | 
					
						
							|  |  |  |         for form in flist: | 
					
						
							|  |  |  |             sectname = "formatter_%s" % form | 
					
						
							|  |  |  |             opts = cp.options(sectname) | 
					
						
							|  |  |  |             if "format" in opts: | 
					
						
							|  |  |  |                 fs = cp.get(sectname, "format", 1) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 fs = None | 
					
						
							|  |  |  |             if "datefmt" in opts: | 
					
						
							|  |  |  |                 dfs = cp.get(sectname, "datefmt", 1) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 dfs = None | 
					
						
							|  |  |  |             f = logging.Formatter(fs, dfs) | 
					
						
							|  |  |  |             formatters[form] = f | 
					
						
							|  |  |  |     #next, do the handlers... | 
					
						
							|  |  |  |     #critical section... | 
					
						
							|  |  |  |     logging._acquireLock() | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             #first, lose the existing handlers... | 
					
						
							|  |  |  |             logging._handlers.clear() | 
					
						
							|  |  |  |             #now set up the new ones... | 
					
						
							|  |  |  |             hlist = cp.get("handlers", "keys") | 
					
						
							|  |  |  |             if len(hlist): | 
					
						
							|  |  |  |                 hlist = string.split(hlist, ",") | 
					
						
							|  |  |  |                 handlers = {} | 
					
						
							|  |  |  |                 fixups = [] #for inter-handler references | 
					
						
							|  |  |  |                 for hand in hlist: | 
					
						
							|  |  |  |                     sectname = "handler_%s" % hand | 
					
						
							|  |  |  |                     klass = cp.get(sectname, "class") | 
					
						
							|  |  |  |                     opts = cp.options(sectname) | 
					
						
							|  |  |  |                     if "formatter" in opts: | 
					
						
							|  |  |  |                         fmt = cp.get(sectname, "formatter") | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         fmt = "" | 
					
						
							|  |  |  |                     klass = eval(klass, vars(logging)) | 
					
						
							|  |  |  |                     args = cp.get(sectname, "args") | 
					
						
							|  |  |  |                     args = eval(args, vars(logging)) | 
					
						
							| 
									
										
										
										
											2003-03-02 20:47:29 +00:00
										 |  |  |                     h = apply(klass, args) | 
					
						
							| 
									
										
										
										
											2002-11-13 16:15:58 +00:00
										 |  |  |                     if "level" in opts: | 
					
						
							|  |  |  |                         level = cp.get(sectname, "level") | 
					
						
							|  |  |  |                         h.setLevel(logging._levelNames[level]) | 
					
						
							|  |  |  |                     if len(fmt): | 
					
						
							|  |  |  |                         h.setFormatter(formatters[fmt]) | 
					
						
							|  |  |  |                     #temporary hack for FileHandler and MemoryHandler. | 
					
						
							|  |  |  |                     if klass == logging.handlers.MemoryHandler: | 
					
						
							|  |  |  |                         if "target" in opts: | 
					
						
							|  |  |  |                             target = cp.get(sectname,"target") | 
					
						
							|  |  |  |                         else: | 
					
						
							|  |  |  |                             target = "" | 
					
						
							|  |  |  |                         if len(target): #the target handler may not be loaded yet, so keep for later... | 
					
						
							|  |  |  |                             fixups.append((h, target)) | 
					
						
							|  |  |  |                     handlers[hand] = h | 
					
						
							|  |  |  |                 #now all handlers are loaded, fixup inter-handler references... | 
					
						
							|  |  |  |                 for fixup in fixups: | 
					
						
							|  |  |  |                     h = fixup[0] | 
					
						
							|  |  |  |                     t = fixup[1] | 
					
						
							|  |  |  |                     h.setTarget(handlers[t]) | 
					
						
							|  |  |  |             #at last, the loggers...first the root... | 
					
						
							|  |  |  |             llist = cp.get("loggers", "keys") | 
					
						
							|  |  |  |             llist = string.split(llist, ",") | 
					
						
							|  |  |  |             llist.remove("root") | 
					
						
							|  |  |  |             sectname = "logger_root" | 
					
						
							|  |  |  |             root = logging.root | 
					
						
							|  |  |  |             log = root | 
					
						
							|  |  |  |             opts = cp.options(sectname) | 
					
						
							|  |  |  |             if "level" in opts: | 
					
						
							|  |  |  |                 level = cp.get(sectname, "level") | 
					
						
							|  |  |  |                 log.setLevel(logging._levelNames[level]) | 
					
						
							| 
									
										
										
										
											2002-12-20 01:54:21 +00:00
										 |  |  |             for h in root.handlers[:]: | 
					
						
							| 
									
										
										
										
											2002-11-13 16:15:58 +00:00
										 |  |  |                 root.removeHandler(h) | 
					
						
							|  |  |  |             hlist = cp.get(sectname, "handlers") | 
					
						
							|  |  |  |             if len(hlist): | 
					
						
							|  |  |  |                 hlist = string.split(hlist, ",") | 
					
						
							|  |  |  |                 for hand in hlist: | 
					
						
							|  |  |  |                     log.addHandler(handlers[hand]) | 
					
						
							|  |  |  |             #and now the others... | 
					
						
							|  |  |  |             #we don't want to lose the existing loggers, | 
					
						
							|  |  |  |             #since other threads may have pointers to them. | 
					
						
							|  |  |  |             #existing is set to contain all existing loggers, | 
					
						
							|  |  |  |             #and as we go through the new configuration we | 
					
						
							|  |  |  |             #remove any which are configured. At the end, | 
					
						
							|  |  |  |             #what's left in existing is the set of loggers | 
					
						
							|  |  |  |             #which were in the previous configuration but | 
					
						
							|  |  |  |             #which are not in the new configuration. | 
					
						
							|  |  |  |             existing = root.manager.loggerDict.keys() | 
					
						
							|  |  |  |             #now set up the new ones... | 
					
						
							|  |  |  |             for log in llist: | 
					
						
							|  |  |  |                 sectname = "logger_%s" % log | 
					
						
							|  |  |  |                 qn = cp.get(sectname, "qualname") | 
					
						
							|  |  |  |                 opts = cp.options(sectname) | 
					
						
							|  |  |  |                 if "propagate" in opts: | 
					
						
							|  |  |  |                     propagate = cp.getint(sectname, "propagate") | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     propagate = 1 | 
					
						
							|  |  |  |                 logger = logging.getLogger(qn) | 
					
						
							|  |  |  |                 if qn in existing: | 
					
						
							|  |  |  |                     existing.remove(qn) | 
					
						
							|  |  |  |                 if "level" in opts: | 
					
						
							|  |  |  |                     level = cp.get(sectname, "level") | 
					
						
							|  |  |  |                     logger.setLevel(logging._levelNames[level]) | 
					
						
							| 
									
										
										
										
											2002-12-20 01:54:21 +00:00
										 |  |  |                 for h in logger.handlers[:]: | 
					
						
							| 
									
										
										
										
											2002-11-13 16:15:58 +00:00
										 |  |  |                     logger.removeHandler(h) | 
					
						
							|  |  |  |                 logger.propagate = propagate | 
					
						
							|  |  |  |                 logger.disabled = 0 | 
					
						
							|  |  |  |                 hlist = cp.get(sectname, "handlers") | 
					
						
							|  |  |  |                 if len(hlist): | 
					
						
							|  |  |  |                     hlist = string.split(hlist, ",") | 
					
						
							|  |  |  |                     for hand in hlist: | 
					
						
							|  |  |  |                         logger.addHandler(handlers[hand]) | 
					
						
							|  |  |  |             #Disable any old loggers. There's no point deleting | 
					
						
							|  |  |  |             #them as other threads may continue to hold references | 
					
						
							|  |  |  |             #and by disabling them, you stop them doing any logging. | 
					
						
							|  |  |  |             for log in existing: | 
					
						
							|  |  |  |                 root.manager.loggerDict[log].disabled = 1 | 
					
						
							|  |  |  |         except: | 
					
						
							|  |  |  |             import traceback | 
					
						
							|  |  |  |             ei = sys.exc_info() | 
					
						
							|  |  |  |             traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr) | 
					
						
							|  |  |  |             del ei | 
					
						
							|  |  |  |     finally: | 
					
						
							|  |  |  |         logging._releaseLock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def listen(port=DEFAULT_LOGGING_CONFIG_PORT): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Start up a socket server on the specified port, and listen for new | 
					
						
							|  |  |  |     configurations. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     These will be sent as a file suitable for processing by fileConfig(). | 
					
						
							|  |  |  |     Returns a Thread object on which you can call start() to start the server, | 
					
						
							|  |  |  |     and which you can join() when appropriate. To stop the server, call | 
					
						
							|  |  |  |     stopListening(). | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not thread: | 
					
						
							|  |  |  |         raise NotImplementedError, "listen() needs threading to work" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class ConfigStreamHandler(StreamRequestHandler): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Handler for a logging configuration request. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         It expects a completely new logging configuration and uses fileConfig | 
					
						
							|  |  |  |         to install it. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         def handle(self): | 
					
						
							|  |  |  |             """
 | 
					
						
							|  |  |  |             Handle a request. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             Each request is expected to be a 4-byte length, | 
					
						
							|  |  |  |             followed by the config file. Uses fileConfig() to do the | 
					
						
							|  |  |  |             grunt work. | 
					
						
							|  |  |  |             """
 | 
					
						
							|  |  |  |             import tempfile | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 conn = self.connection | 
					
						
							|  |  |  |                 chunk = conn.recv(4) | 
					
						
							|  |  |  |                 if len(chunk) == 4: | 
					
						
							|  |  |  |                     slen = struct.unpack(">L", chunk)[0] | 
					
						
							|  |  |  |                     chunk = self.connection.recv(slen) | 
					
						
							|  |  |  |                     while len(chunk) < slen: | 
					
						
							|  |  |  |                         chunk = chunk + conn.recv(slen - len(chunk)) | 
					
						
							|  |  |  |                     #Apply new configuration. We'd like to be able to | 
					
						
							|  |  |  |                     #create a StringIO and pass that in, but unfortunately | 
					
						
							|  |  |  |                     #1.5.2 ConfigParser does not support reading file | 
					
						
							|  |  |  |                     #objects, only actual files. So we create a temporary | 
					
						
							|  |  |  |                     #file and remove it later. | 
					
						
							|  |  |  |                     file = tempfile.mktemp(".ini") | 
					
						
							|  |  |  |                     f = open(file, "w") | 
					
						
							|  |  |  |                     f.write(chunk) | 
					
						
							|  |  |  |                     f.close() | 
					
						
							|  |  |  |                     fileConfig(file) | 
					
						
							|  |  |  |                     os.remove(file) | 
					
						
							|  |  |  |             except socket.error, e: | 
					
						
							|  |  |  |                 if type(e.args) != types.TupleType: | 
					
						
							|  |  |  |                     raise | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     errcode = e.args[0] | 
					
						
							|  |  |  |                     if errcode != RESET_ERROR: | 
					
						
							|  |  |  |                         raise | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class ConfigSocketReceiver(ThreadingTCPServer): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         A simple TCP socket-based logging config receiver. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         allow_reuse_address = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT, | 
					
						
							| 
									
										
										
										
											2002-11-15 23:33:20 +00:00
										 |  |  |                      handler=None): | 
					
						
							| 
									
										
										
										
											2002-11-13 16:15:58 +00:00
										 |  |  |             ThreadingTCPServer.__init__(self, (host, port), handler) | 
					
						
							|  |  |  |             logging._acquireLock() | 
					
						
							|  |  |  |             self.abort = 0 | 
					
						
							|  |  |  |             logging._releaseLock() | 
					
						
							|  |  |  |             self.timeout = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def serve_until_stopped(self): | 
					
						
							|  |  |  |             import select | 
					
						
							|  |  |  |             abort = 0 | 
					
						
							|  |  |  |             while not abort: | 
					
						
							|  |  |  |                 rd, wr, ex = select.select([self.socket.fileno()], | 
					
						
							|  |  |  |                                            [], [], | 
					
						
							|  |  |  |                                            self.timeout) | 
					
						
							|  |  |  |                 if rd: | 
					
						
							|  |  |  |                     self.handle_request() | 
					
						
							|  |  |  |                 logging._acquireLock() | 
					
						
							|  |  |  |                 abort = self.abort | 
					
						
							|  |  |  |                 logging._releaseLock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-15 23:33:20 +00:00
										 |  |  |     def serve(rcvr, hdlr, port): | 
					
						
							|  |  |  |         server = rcvr(port=port, handler=hdlr) | 
					
						
							| 
									
										
										
										
											2002-11-13 16:15:58 +00:00
										 |  |  |         global _listener | 
					
						
							|  |  |  |         logging._acquireLock() | 
					
						
							|  |  |  |         _listener = server | 
					
						
							|  |  |  |         logging._releaseLock() | 
					
						
							|  |  |  |         server.serve_until_stopped() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-15 23:33:20 +00:00
										 |  |  |     return threading.Thread(target=serve, | 
					
						
							|  |  |  |                             args=(ConfigSocketReceiver, | 
					
						
							|  |  |  |                                   ConfigStreamHandler, port)) | 
					
						
							| 
									
										
										
										
											2002-11-13 16:15:58 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def stopListening(): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Stop the listening server which was created with a call to listen(). | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2002-11-15 23:33:20 +00:00
										 |  |  |     global _listener | 
					
						
							| 
									
										
										
										
											2002-11-13 16:15:58 +00:00
										 |  |  |     if _listener: | 
					
						
							|  |  |  |         logging._acquireLock() | 
					
						
							|  |  |  |         _listener.abort = 1 | 
					
						
							|  |  |  |         _listener = None | 
					
						
							|  |  |  |         logging._releaseLock() |