summaryrefslogtreecommitdiffstats
path: root/Lib/logging/config.py
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2002-11-13 16:15:58 (GMT)
committerGuido van Rossum <guido@python.org>2002-11-13 16:15:58 (GMT)
commit57102f861d506b6c2d2215d100dac9143574fa77 (patch)
tree7c138b530db1434d3dc54145ffad3eb4ea50185d /Lib/logging/config.py
parent2c184e7eea98ce59f996b76755e1c6142986933b (diff)
downloadcpython-57102f861d506b6c2d2215d100dac9143574fa77.zip
cpython-57102f861d506b6c2d2215d100dac9143574fa77.tar.gz
cpython-57102f861d506b6c2d2215d100dac9143574fa77.tar.bz2
Adding Vinay Sajip's logging package.
Diffstat (limited to 'Lib/logging/config.py')
-rw-r--r--Lib/logging/config.py300
1 files changed, 300 insertions, 0 deletions
diff --git a/Lib/logging/config.py b/Lib/logging/config.py
new file mode 100644
index 0000000..285dfdd
--- /dev/null
+++ b/Lib/logging/config.py
@@ -0,0 +1,300 @@
+#! /usr/bin/env python
+#
+# 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.
+#
+# For the change history, see README.txt in the distribution.
+#
+# This file is part of the Python logging distribution. See
+# http://www.red-dove.com/python_logging.html
+#
+
+"""
+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
+
+def fileConfig(fname):
+ """
+ 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
+
+ cp = ConfigParser.ConfigParser()
+ 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))
+ h = apply(klass, args)
+ 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])
+ for h in root.handlers:
+ 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])
+ for h in logger.handlers:
+ 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,
+ handler=None):
+ 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()
+
+ def serve(rcvr, hdlr):
+ server = rcvr(handler=hdlr)
+ global _listener
+ logging._acquireLock()
+ _listener = server
+ logging._releaseLock()
+ server.serve_until_stopped()
+
+ return threading.Thread(target=serve, args=(ConfigSocketReceiver, ConfigStreamHandler))
+
+def stopListening():
+ """
+ Stop the listening server which was created with a call to listen().
+ """
+ if _listener:
+ logging._acquireLock()
+ _listener.abort = 1
+ _listener = None
+ logging._releaseLock()