0001 # Copyright 2001-2004 by Vinay Sajip. All Rights Reserved. 0002 # 0003 # Permission to use, copy, modify, and distribute this software and its 0004 # documentation for any purpose and without fee is hereby granted, 0005 # provided that the above copyright notice appear in all copies and that 0006 # both that copyright notice and this permission notice appear in 0007 # supporting documentation, and that the name of Vinay Sajip 0008 # not be used in advertising or publicity pertaining to distribution 0009 # of the software without specific, written prior permission. 0010 # VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 0011 # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 0012 # VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 0013 # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 0014 # IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 0015 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 0016 0017 """ 0018 Configuration functions for the logging package for Python. The core package 0019 is based on PEP 282 and comments thereto in comp.lang.python, and influenced 0020 by Apache's log4j system. 0021 0022 Should work under Python versions >= 1.5.2, except that source line 0023 information is not available unless 'sys._getframe()' is. 0024 0025 Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved. 0026 0027 To use, simply 'import logging' and log away! 0028 """ 0029 0030 import sys, logging, logging.handlers, string, thread, threading, socket, struct, os 0031 0032 from SocketServer import ThreadingTCPServer, StreamRequestHandler 0033 0034 0035 DEFAULT_LOGGING_CONFIG_PORT = 9030 0036 0037 if sys.platform == "win32": 0038 RESET_ERROR = 10054 #WSAECONNRESET 0039 else: 0040 RESET_ERROR = 104 #ECONNRESET 0041 0042 # 0043 # The following code implements a socket listener for on-the-fly 0044 # reconfiguration of logging. 0045 # 0046 # _listener holds the server object doing the listening 0047 _listener = None 0048 0049 def fileConfig(fname, defaults=None): 0050 """ 0051 Read the logging configuration from a ConfigParser-format file. 0052 0053 This can be called several times from an application, allowing an end user 0054 the ability to select from various pre-canned configurations (if the 0055 developer provides a mechanism to present the choices and load the chosen 0056 configuration). 0057 In versions of ConfigParser which have the readfp method [typically 0058 shipped in 2.x versions of Python], you can pass in a file-like object 0059 rather than a filename, in which case the file-like object will be read 0060 using readfp. 0061 """ 0062 import ConfigParser 0063 0064 cp = ConfigParser.ConfigParser(defaults) 0065 if hasattr(cp, 'readfp') and hasattr(fname, 'readline'): 0066 cp.readfp(fname) 0067 else: 0068 cp.read(fname) 0069 #first, do the formatters... 0070 flist = cp.get("formatters", "keys") 0071 if len(flist): 0072 flist = string.split(flist, ",") 0073 formatters = {} 0074 for form in flist: 0075 sectname = "formatter_%s" % form 0076 opts = cp.options(sectname) 0077 if "format" in opts: 0078 fs = cp.get(sectname, "format", 1) 0079 else: 0080 fs = None 0081 if "datefmt" in opts: 0082 dfs = cp.get(sectname, "datefmt", 1) 0083 else: 0084 dfs = None 0085 f = logging.Formatter(fs, dfs) 0086 formatters[form] = f 0087 #next, do the handlers... 0088 #critical section... 0089 logging._acquireLock() 0090 try: 0091 try: 0092 #first, lose the existing handlers... 0093 logging._handlers.clear() 0094 #now set up the new ones... 0095 hlist = cp.get("handlers", "keys") 0096 if len(hlist): 0097 hlist = string.split(hlist, ",") 0098 handlers = {} 0099 fixups = [] #for inter-handler references 0100 for hand in hlist: 0101 try: 0102 sectname = "handler_%s" % hand 0103 klass = cp.get(sectname, "class") 0104 opts = cp.options(sectname) 0105 if "formatter" in opts: 0106 fmt = cp.get(sectname, "formatter") 0107 else: 0108 fmt = "" 0109 klass = eval(klass, vars(logging)) 0110 args = cp.get(sectname, "args") 0111 args = eval(args, vars(logging)) 0112 h = apply(klass, args) 0113 if "level" in opts: 0114 level = cp.get(sectname, "level") 0115 h.setLevel(logging._levelNames[level]) 0116 if len(fmt): 0117 h.setFormatter(formatters[fmt]) 0118 #temporary hack for FileHandler and MemoryHandler. 0119 if klass == logging.handlers.MemoryHandler: 0120 if "target" in opts: 0121 target = cp.get(sectname,"target") 0122 else: 0123 target = "" 0124 if len(target): #the target handler may not be loaded yet, so keep for later... 0125 fixups.append((h, target)) 0126 handlers[hand] = h 0127 except: #if an error occurs when instantiating a handler, too bad 0128 pass #this could happen e.g. because of lack of privileges 0129 #now all handlers are loaded, fixup inter-handler references... 0130 for fixup in fixups: 0131 h = fixup[0] 0132 t = fixup[1] 0133 h.setTarget(handlers[t]) 0134 #at last, the loggers...first the root... 0135 llist = cp.get("loggers", "keys") 0136 llist = string.split(llist, ",") 0137 llist.remove("root") 0138 sectname = "logger_root" 0139 root = logging.root 0140 log = root 0141 opts = cp.options(sectname) 0142 if "level" in opts: 0143 level = cp.get(sectname, "level") 0144 log.setLevel(logging._levelNames[level]) 0145 for h in root.handlers[:]: 0146 root.removeHandler(h) 0147 hlist = cp.get(sectname, "handlers") 0148 if len(hlist): 0149 hlist = string.split(hlist, ",") 0150 for hand in hlist: 0151 log.addHandler(handlers[hand]) 0152 #and now the others... 0153 #we don't want to lose the existing loggers, 0154 #since other threads may have pointers to them. 0155 #existing is set to contain all existing loggers, 0156 #and as we go through the new configuration we 0157 #remove any which are configured. At the end, 0158 #what's left in existing is the set of loggers 0159 #which were in the previous configuration but 0160 #which are not in the new configuration. 0161 existing = root.manager.loggerDict.keys() 0162 #now set up the new ones... 0163 for log in llist: 0164 sectname = "logger_%s" % log 0165 qn = cp.get(sectname, "qualname") 0166 opts = cp.options(sectname) 0167 if "propagate" in opts: 0168 propagate = cp.getint(sectname, "propagate") 0169 else: 0170 propagate = 1 0171 logger = logging.getLogger(qn) 0172 if qn in existing: 0173 existing.remove(qn) 0174 if "level" in opts: 0175 level = cp.get(sectname, "level") 0176 logger.setLevel(logging._levelNames[level]) 0177 for h in logger.handlers[:]: 0178 logger.removeHandler(h) 0179 logger.propagate = propagate 0180 logger.disabled = 0 0181 hlist = cp.get(sectname, "handlers") 0182 if len(hlist): 0183 hlist = string.split(hlist, ",") 0184 for hand in hlist: 0185 logger.addHandler(handlers[hand]) 0186 #Disable any old loggers. There's no point deleting 0187 #them as other threads may continue to hold references 0188 #and by disabling them, you stop them doing any logging. 0189 for log in existing: 0190 root.manager.loggerDict[log].disabled = 1 0191 except: 0192 import traceback 0193 ei = sys.exc_info() 0194 traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr) 0195 del ei 0196 finally: 0197 logging._releaseLock() 0198 0199 def listen(port=DEFAULT_LOGGING_CONFIG_PORT): 0200 """ 0201 Start up a socket server on the specified port, and listen for new 0202 configurations. 0203 0204 These will be sent as a file suitable for processing by fileConfig(). 0205 Returns a Thread object on which you can call start() to start the server, 0206 and which you can join() when appropriate. To stop the server, call 0207 stopListening(). 0208 """ 0209 if not thread: 0210 raise NotImplementedError, "listen() needs threading to work" 0211 0212 class ConfigStreamHandler(StreamRequestHandler): 0213 """ 0214 Handler for a logging configuration request. 0215 0216 It expects a completely new logging configuration and uses fileConfig 0217 to install it. 0218 """ 0219 def handle(self): 0220 """ 0221 Handle a request. 0222 0223 Each request is expected to be a 4-byte length, 0224 followed by the config file. Uses fileConfig() to do the 0225 grunt work. 0226 """ 0227 import tempfile 0228 try: 0229 conn = self.connection 0230 chunk = conn.recv(4) 0231 if len(chunk) == 4: 0232 slen = struct.unpack(">L", chunk)[0] 0233 chunk = self.connection.recv(slen) 0234 while len(chunk) < slen: 0235 chunk = chunk + conn.recv(slen - len(chunk)) 0236 #Apply new configuration. We'd like to be able to 0237 #create a StringIO and pass that in, but unfortunately 0238 #1.5.2 ConfigParser does not support reading file 0239 #objects, only actual files. So we create a temporary 0240 #file and remove it later. 0241 file = tempfile.mktemp(".ini") 0242 f = open(file, "w") 0243 f.write(chunk) 0244 f.close() 0245 fileConfig(file) 0246 os.remove(file) 0247 except socket.error, e: 0248 if type(e.args) != types.TupleType: 0249 raise 0250 else: 0251 errcode = e.args[0] 0252 if errcode != RESET_ERROR: 0253 raise 0254 0255 class ConfigSocketReceiver(ThreadingTCPServer): 0256 """ 0257 A simple TCP socket-based logging config receiver. 0258 """ 0259 0260 allow_reuse_address = 1 0261 0262 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT, 0263 handler=None): 0264 ThreadingTCPServer.__init__(self, (host, port), handler) 0265 logging._acquireLock() 0266 self.abort = 0 0267 logging._releaseLock() 0268 self.timeout = 1 0269 0270 def serve_until_stopped(self): 0271 import select 0272 abort = 0 0273 while not abort: 0274 rd, wr, ex = select.select([self.socket.fileno()], 0275 [], [], 0276 self.timeout) 0277 if rd: 0278 self.handle_request() 0279 logging._acquireLock() 0280 abort = self.abort 0281 logging._releaseLock() 0282 0283 def serve(rcvr, hdlr, port): 0284 server = rcvr(port=port, handler=hdlr) 0285 global _listener 0286 logging._acquireLock() 0287 _listener = server 0288 logging._releaseLock() 0289 server.serve_until_stopped() 0290 0291 return threading.Thread(target=serve, 0292 args=(ConfigSocketReceiver, 0293 ConfigStreamHandler, port)) 0294 0295 def stopListening(): 0296 """ 0297 Stop the listening server which was created with a call to listen(). 0298 """ 0299 global _listener 0300 if _listener: 0301 logging._acquireLock() 0302 _listener.abort = 1 0303 _listener = None 0304 logging._releaseLock() 0305
Generated by PyXR 0.9.4