1 # Copyright 2001-2005 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.
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.
22 Should work under Python versions >= 1.5.2, except that source line
23 information is not available unless 'sys._getframe()' is.
25 Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved.
27 To use, simply 'import logging' and log away!
30 import sys
, logging
, logging
.handlers
, string
, socket
, struct
, os
, traceback
38 from SocketServer
import ThreadingTCPServer
, StreamRequestHandler
41 DEFAULT_LOGGING_CONFIG_PORT
= 9030
43 if sys
.platform
== "win32":
44 RESET_ERROR
= 10054 #WSAECONNRESET
46 RESET_ERROR
= 104 #ECONNRESET
49 # The following code implements a socket listener for on-the-fly
50 # reconfiguration of logging.
52 # _listener holds the server object doing the listening
55 def fileConfig(fname
, defaults
=None):
57 Read the logging configuration from a ConfigParser-format file.
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
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
70 cp
= ConfigParser
.ConfigParser(defaults
)
71 if hasattr(cp
, 'readfp') and hasattr(fname
, 'readline'):
76 formatters
= _create_formatters(cp
)
79 logging
._acquireLock
()
81 logging
._handlers
.clear()
82 # Handlers add themselves to logging._handlers
83 handlers
= _install_handlers(cp
, formatters
)
84 _install_loggers(cp
, handlers
)
86 logging
._releaseLock
()
90 """Resolve a dotted name to a global object."""
91 name
= string
.split(name
, '.')
93 found
= __import__(used
)
97 found
= getattr(found
, n
)
98 except AttributeError:
100 found
= getattr(found
, n
)
104 def _create_formatters(cp
):
105 """Create and return formatters"""
106 flist
= cp
.get("formatters", "keys")
109 flist
= string
.split(flist
, ",")
112 sectname
= "formatter_%s" % form
113 opts
= cp
.options(sectname
)
115 fs
= cp
.get(sectname
, "format", 1)
118 if "datefmt" in opts
:
119 dfs
= cp
.get(sectname
, "datefmt", 1)
122 c
= logging
.Formatter
124 class_name
= cp
.get(sectname
, "class")
126 c
= _resolve(class_name
)
132 def _install_handlers(cp
, formatters
):
133 """Install and return handlers"""
134 hlist
= cp
.get("handlers", "keys")
137 hlist
= string
.split(hlist
, ",")
139 fixups
= [] #for inter-handler references
141 sectname
= "handler_%s" % hand
142 klass
= cp
.get(sectname
, "class")
143 opts
= cp
.options(sectname
)
144 if "formatter" in opts
:
145 fmt
= cp
.get(sectname
, "formatter")
148 klass
= eval(klass
, vars(logging
))
149 args
= cp
.get(sectname
, "args")
150 args
= eval(args
, vars(logging
))
151 h
= apply(klass
, args
)
153 level
= cp
.get(sectname
, "level")
154 h
.setLevel(logging
._levelNames
[level
])
156 h
.setFormatter(formatters
[fmt
])
157 #temporary hack for FileHandler and MemoryHandler.
158 if klass
== logging
.handlers
.MemoryHandler
:
160 target
= cp
.get(sectname
,"target")
163 if len(target
): #the target handler may not be loaded yet, so keep for later...
164 fixups
.append((h
, target
))
166 #now all handlers are loaded, fixup inter-handler references...
168 h
.setTarget(handlers
[t
])
172 def _install_loggers(cp
, handlers
):
173 """Create and install loggers"""
175 # configure the root first
176 llist
= cp
.get("loggers", "keys")
177 llist
= string
.split(llist
, ",")
179 sectname
= "logger_root"
182 opts
= cp
.options(sectname
)
184 level
= cp
.get(sectname
, "level")
185 log
.setLevel(logging
._levelNames
[level
])
186 for h
in root
.handlers
[:]:
187 root
.removeHandler(h
)
188 hlist
= cp
.get(sectname
, "handlers")
190 hlist
= string
.split(hlist
, ",")
192 log
.addHandler(handlers
[hand
])
194 #and now the others...
195 #we don't want to lose the existing loggers,
196 #since other threads may have pointers to them.
197 #existing is set to contain all existing loggers,
198 #and as we go through the new configuration we
199 #remove any which are configured. At the end,
200 #what's left in existing is the set of loggers
201 #which were in the previous configuration but
202 #which are not in the new configuration.
203 existing
= root
.manager
.loggerDict
.keys()
204 #now set up the new ones...
206 sectname
= "logger_%s" % log
207 qn
= cp
.get(sectname
, "qualname")
208 opts
= cp
.options(sectname
)
209 if "propagate" in opts
:
210 propagate
= cp
.getint(sectname
, "propagate")
213 logger
= logging
.getLogger(qn
)
217 level
= cp
.get(sectname
, "level")
218 logger
.setLevel(logging
._levelNames
[level
])
219 for h
in logger
.handlers
[:]:
220 logger
.removeHandler(h
)
221 logger
.propagate
= propagate
223 hlist
= cp
.get(sectname
, "handlers")
225 hlist
= string
.split(hlist
, ",")
227 logger
.addHandler(handlers
[hand
])
229 #Disable any old loggers. There's no point deleting
230 #them as other threads may continue to hold references
231 #and by disabling them, you stop them doing any logging.
233 root
.manager
.loggerDict
[log
].disabled
= 1
236 def listen(port
=DEFAULT_LOGGING_CONFIG_PORT
):
238 Start up a socket server on the specified port, and listen for new
241 These will be sent as a file suitable for processing by fileConfig().
242 Returns a Thread object on which you can call start() to start the server,
243 and which you can join() when appropriate. To stop the server, call
247 raise NotImplementedError, "listen() needs threading to work"
249 class ConfigStreamHandler(StreamRequestHandler
):
251 Handler for a logging configuration request.
253 It expects a completely new logging configuration and uses fileConfig
260 Each request is expected to be a 4-byte length, packed using
261 struct.pack(">L", n), followed by the config file.
262 Uses fileConfig() to do the grunt work.
266 conn
= self
.connection
269 slen
= struct
.unpack(">L", chunk
)[0]
270 chunk
= self
.connection
.recv(slen
)
271 while len(chunk
) < slen
:
272 chunk
= chunk
+ conn
.recv(slen
- len(chunk
))
273 #Apply new configuration. We'd like to be able to
274 #create a StringIO and pass that in, but unfortunately
275 #1.5.2 ConfigParser does not support reading file
276 #objects, only actual files. So we create a temporary
277 #file and remove it later.
278 file = tempfile
.mktemp(".ini")
284 except (KeyboardInterrupt, SystemExit):
287 traceback
.print_exc()
289 except socket
.error
, e
:
290 if type(e
.args
) != types
.TupleType
:
294 if errcode
!= RESET_ERROR
:
297 class ConfigSocketReceiver(ThreadingTCPServer
):
299 A simple TCP socket-based logging config receiver.
302 allow_reuse_address
= 1
304 def __init__(self
, host
='localhost', port
=DEFAULT_LOGGING_CONFIG_PORT
,
306 ThreadingTCPServer
.__init
__(self
, (host
, port
), handler
)
307 logging
._acquireLock
()
309 logging
._releaseLock
()
312 def serve_until_stopped(self
):
316 rd
, wr
, ex
= select
.select([self
.socket
.fileno()],
320 self
.handle_request()
321 logging
._acquireLock
()
323 logging
._releaseLock
()
325 def serve(rcvr
, hdlr
, port
):
326 server
= rcvr(port
=port
, handler
=hdlr
)
328 logging
._acquireLock
()
330 logging
._releaseLock
()
331 server
.serve_until_stopped()
333 return threading
.Thread(target
=serve
,
334 args
=(ConfigSocketReceiver
,
335 ConfigStreamHandler
, port
))
339 Stop the listening server which was created with a call to listen().
343 logging
._acquireLock
()
346 logging
._releaseLock
()