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