[Zope3-checkins] CVS: Zope3/src/logging - __init__.py:1.2 config.py:1.2 handlers.py:1.2
Jim Fulton
jim@zope.com
Wed, 25 Dec 2002 09:13:43 -0500
Update of /cvs-repository/Zope3/src/logging
In directory cvs.zope.org:/tmp/cvs-serv15352/src/logging
Added Files:
__init__.py config.py handlers.py
Log Message:
Grand renaming:
- Renamed most files (especially python modules) to lower case.
- Moved views and interfaces into separate hierarchies within each
project, where each top-level directory under the zope package
is a separate project.
- Moved everything to src from lib/python.
lib/python will eventually go away. I need access to the cvs
repository to make this happen, however.
There are probably some bits that are broken. All tests pass
and zope runs, but I haven't tried everything. There are a number
of cleanups I'll work on tomorrow.
=== Zope3/src/logging/__init__.py 1.1 => 1.2 === (1086/1186 lines abridged)
--- /dev/null Wed Dec 25 09:13:43 2002
+++ Zope3/src/logging/__init__.py Wed Dec 25 09:12:12 2002
@@ -0,0 +1,1183 @@
+#! /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, os, types, time, string, struct, cPickle, cStringIO
+
+try:
+ import thread
+ import threading
+except ImportError:
+ thread = None
+try:
+ import inspect
+except ImportError:
+ inspect = None
[-=- -=- -=- 1086 lines omitted -=- -=- -=-]
+def exception(msg, *args):
+ """
+ Log a message with severity 'ERROR' on the root logger,
+ with exception information.
+ """
+ apply(error, (msg,)+args, {'exc_info': 1})
+
+def warn(msg, *args, **kwargs):
+ """
+ Log a message with severity 'WARN' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ apply(root.warn, (msg,)+args, kwargs)
+
+def info(msg, *args, **kwargs):
+ """
+ Log a message with severity 'INFO' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ apply(root.info, (msg,)+args, kwargs)
+
+def debug(msg, *args, **kwargs):
+ """
+ Log a message with severity 'DEBUG' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ apply(root.debug, (msg,)+args, kwargs)
+
+def disable(level):
+ """
+ Disable all logging calls less severe than 'level'.
+ """
+ root.manager.disable = level
+
+def shutdown():
+ """
+ Perform any cleanup actions in the logging system (e.g. flushing
+ buffers).
+
+ Should be called at application exit.
+ """
+ for h in _handlers.keys():
+ h.flush()
+ h.close()
+
+if __name__ == "__main__":
+ print __doc__
=== Zope3/src/logging/config.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:13:43 2002
+++ Zope3/src/logging/config.py Wed Dec 25 09:12:12 2002
@@ -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,defaults=None):
+ """
+ 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(defaults)
+ 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()
=== Zope3/src/logging/handlers.py 1.1 => 1.2 === (613/713 lines abridged)
--- /dev/null Wed Dec 25 09:13:43 2002
+++ Zope3/src/logging/handlers.py Wed Dec 25 09:12:12 2002
@@ -0,0 +1,710 @@
+#! /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, socket, types, os, string, cPickle, struct
+
+from SocketServer import ThreadingTCPServer, StreamRequestHandler
+
+#
+# Some constants...
+#
+
+DEFAULT_TCP_LOGGING_PORT = 9020
+DEFAULT_UDP_LOGGING_PORT = 9021
+DEFAULT_HTTP_LOGGING_PORT = 9022
[-=- -=- -=- 613 lines omitted -=- -=- -=-]
+
+class MemoryHandler(BufferingHandler):
+ """
+ A handler class which buffers logging records in memory, periodically
+ flushing them to a target handler. Flushing occurs whenever the buffer
+ is full, or when an event of a certain severity or greater is seen.
+ """
+ def __init__(self, capacity, flushLevel=logging.ERROR, target=None):
+ """
+ Initialize the handler with the buffer size, the level at which
+ flushing should occur and an optional target.
+
+ Note that without a target being set either here or via setTarget(),
+ a MemoryHandler is no use to anyone!
+ """
+ BufferingHandler.__init__(self, capacity)
+ self.flushLevel = flushLevel
+ self.target = target
+
+ def shouldFlush(self, record):
+ """
+ Check for buffer full or a record at the flushLevel or higher.
+ """
+ return (len(self.buffer) >= self.capacity) or \
+ (record.levelno >= self.flushLevel)
+
+ def setTarget(self, target):
+ """
+ Set the target handler for this handler.
+ """
+ self.target = target
+
+ def flush(self):
+ """
+ For a MemoryHandler, flushing means just sending the buffered
+ records to the target, if there is one. Override if you want
+ different behaviour.
+ """
+ if self.target:
+ for record in self.buffer:
+ self.target.handle(record)
+ self.buffer = []
+
+ def close(self):
+ """
+ Flush, set the target to None and lose the buffer.
+ """
+ self.flush()
+ self.target = None
+ self.buffer = []