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.
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!
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
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
47 def fileConfig(fname
, defaults
=None):
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
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
62 cp
= ConfigParser
.ConfigParser(defaults
)
63 if hasattr(cp
, 'readfp') and hasattr(fname
, 'readline'):
67 #first, do the formatters...
68 flist
= cp
.get("formatters", "keys")
70 flist
= string
.split(flist
, ",")
73 sectname
= "formatter_%s" % form
74 opts
= cp
.options(sectname
)
76 fs
= cp
.get(sectname
, "format", 1)
80 dfs
= cp
.get(sectname
, "datefmt", 1)
83 f
= logging
.Formatter(fs
, dfs
)
85 #next, do the handlers...
87 logging
._acquireLock
()
90 #first, lose the existing handlers...
91 logging
._handlers
.clear()
92 #now set up the new ones...
93 hlist
= cp
.get("handlers", "keys")
95 hlist
= string
.split(hlist
, ",")
97 fixups
= [] #for inter-handler references
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")
106 klass
= eval(klass
, vars(logging
))
107 args
= cp
.get(sectname
, "args")
108 args
= eval(args
, vars(logging
))
109 h
= apply(klass
, args
)
111 level
= cp
.get(sectname
, "level")
112 h
.setLevel(logging
._levelNames
[level
])
114 h
.setFormatter(formatters
[fmt
])
115 #temporary hack for FileHandler and MemoryHandler.
116 if klass
== logging
.handlers
.MemoryHandler
:
118 target
= cp
.get(sectname
,"target")
121 if len(target
): #the target handler may not be loaded yet, so keep for later...
122 fixups
.append((h
, target
))
124 #now all handlers are loaded, fixup inter-handler references...
128 h
.setTarget(handlers
[t
])
129 #at last, the loggers...first the root...
130 llist
= cp
.get("loggers", "keys")
131 llist
= string
.split(llist
, ",")
133 sectname
= "logger_root"
136 opts
= cp
.options(sectname
)
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")
144 hlist
= string
.split(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...
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")
166 logger
= logging
.getLogger(qn
)
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
176 hlist
= cp
.get(sectname
, "handlers")
178 hlist
= string
.split(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.
185 root
.manager
.loggerDict
[log
].disabled
= 1
189 traceback
.print_exception(ei
[0], ei
[1], ei
[2], None, sys
.stderr
)
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
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
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
218 Each request is expected to be a 4-byte length,
219 followed by the config file. Uses fileConfig() to do the
224 conn
= self
.connection
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")
242 except socket
.error
, e
:
243 if type(e
.args
) != types
.TupleType
:
247 if errcode
!= RESET_ERROR
:
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
,
259 ThreadingTCPServer
.__init
__(self
, (host
, port
), handler
)
260 logging
._acquireLock
()
262 logging
._releaseLock
()
265 def serve_until_stopped(self
):
269 rd
, wr
, ex
= select
.select([self
.socket
.fileno()],
273 self
.handle_request()
274 logging
._acquireLock
()
276 logging
._releaseLock
()
278 def serve(rcvr
, hdlr
, port
):
279 server
= rcvr(port
=port
, handler
=hdlr
)
281 logging
._acquireLock
()
283 logging
._releaseLock
()
284 server
.serve_until_stopped()
286 return threading
.Thread(target
=serve
,
287 args
=(ConfigSocketReceiver
,
288 ConfigStreamHandler
, port
))
292 Stop the listening server which was created with a call to listen().
296 logging
._acquireLock
()
299 logging
._releaseLock
()