summaryrefslogtreecommitdiff
path: root/lib/python2.7/logging/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python2.7/logging/__init__.py')
-rw-r--r--lib/python2.7/logging/__init__.py1726
1 files changed, 1726 insertions, 0 deletions
diff --git a/lib/python2.7/logging/__init__.py b/lib/python2.7/logging/__init__.py
new file mode 100644
index 0000000..1a622a3
--- /dev/null
+++ b/lib/python2.7/logging/__init__.py
@@ -0,0 +1,1726 @@
+# Copyright 2001-2012 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.
+
+"""
+Logging package for Python. Based on PEP 282 and comments thereto in
+comp.lang.python.
+
+Copyright (C) 2001-2012 Vinay Sajip. All Rights Reserved.
+
+To use, simply 'import logging' and log away!
+"""
+
+import sys, os, time, cStringIO, traceback, warnings, weakref
+
+__all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR',
+ 'FATAL', 'FileHandler', 'Filter', 'Formatter', 'Handler', 'INFO',
+ 'LogRecord', 'Logger', 'LoggerAdapter', 'NOTSET', 'NullHandler',
+ 'StreamHandler', 'WARN', 'WARNING', 'addLevelName', 'basicConfig',
+ 'captureWarnings', 'critical', 'debug', 'disable', 'error',
+ 'exception', 'fatal', 'getLevelName', 'getLogger', 'getLoggerClass',
+ 'info', 'log', 'makeLogRecord', 'setLoggerClass', 'warn', 'warning']
+
+try:
+ import codecs
+except ImportError:
+ codecs = None
+
+try:
+ import thread
+ import threading
+except ImportError:
+ thread = None
+
+__author__ = "Vinay Sajip <vinay_sajip@red-dove.com>"
+__status__ = "production"
+__version__ = "0.5.1.2"
+__date__ = "07 February 2010"
+
+#---------------------------------------------------------------------------
+# Miscellaneous module data
+#---------------------------------------------------------------------------
+try:
+ unicode
+ _unicode = True
+except NameError:
+ _unicode = False
+
+#
+# _srcfile is used when walking the stack to check when we've got the first
+# caller stack frame.
+#
+if hasattr(sys, 'frozen'): #support for py2exe
+ _srcfile = "logging%s__init__%s" % (os.sep, __file__[-4:])
+elif __file__[-4:].lower() in ['.pyc', '.pyo']:
+ _srcfile = __file__[:-4] + '.py'
+else:
+ _srcfile = __file__
+_srcfile = os.path.normcase(_srcfile)
+
+# next bit filched from 1.5.2's inspect.py
+def currentframe():
+ """Return the frame object for the caller's stack frame."""
+ try:
+ raise Exception
+ except:
+ return sys.exc_info()[2].tb_frame.f_back
+
+if hasattr(sys, '_getframe'): currentframe = lambda: sys._getframe(3)
+# done filching
+
+# _srcfile is only used in conjunction with sys._getframe().
+# To provide compatibility with older versions of Python, set _srcfile
+# to None if _getframe() is not available; this value will prevent
+# findCaller() from being called.
+#if not hasattr(sys, "_getframe"):
+# _srcfile = None
+
+#
+#_startTime is used as the base when calculating the relative time of events
+#
+_startTime = time.time()
+
+#
+#raiseExceptions is used to see if exceptions during handling should be
+#propagated
+#
+raiseExceptions = 1
+
+#
+# If you don't want threading information in the log, set this to zero
+#
+logThreads = 1
+
+#
+# If you don't want multiprocessing information in the log, set this to zero
+#
+logMultiprocessing = 1
+
+#
+# If you don't want process information in the log, set this to zero
+#
+logProcesses = 1
+
+#---------------------------------------------------------------------------
+# Level related stuff
+#---------------------------------------------------------------------------
+#
+# Default levels and level names, these can be replaced with any positive set
+# of values having corresponding names. There is a pseudo-level, NOTSET, which
+# is only really there as a lower limit for user-defined levels. Handlers and
+# loggers are initialized with NOTSET so that they will log all messages, even
+# at user-defined levels.
+#
+
+CRITICAL = 50
+FATAL = CRITICAL
+ERROR = 40
+WARNING = 30
+WARN = WARNING
+INFO = 20
+DEBUG = 10
+NOTSET = 0
+
+_levelNames = {
+ CRITICAL : 'CRITICAL',
+ ERROR : 'ERROR',
+ WARNING : 'WARNING',
+ INFO : 'INFO',
+ DEBUG : 'DEBUG',
+ NOTSET : 'NOTSET',
+ 'CRITICAL' : CRITICAL,
+ 'ERROR' : ERROR,
+ 'WARN' : WARNING,
+ 'WARNING' : WARNING,
+ 'INFO' : INFO,
+ 'DEBUG' : DEBUG,
+ 'NOTSET' : NOTSET,
+}
+
+def getLevelName(level):
+ """
+ Return the textual representation of logging level 'level'.
+
+ If the level is one of the predefined levels (CRITICAL, ERROR, WARNING,
+ INFO, DEBUG) then you get the corresponding string. If you have
+ associated levels with names using addLevelName then the name you have
+ associated with 'level' is returned.
+
+ If a numeric value corresponding to one of the defined levels is passed
+ in, the corresponding string representation is returned.
+
+ Otherwise, the string "Level %s" % level is returned.
+ """
+ return _levelNames.get(level, ("Level %s" % level))
+
+def addLevelName(level, levelName):
+ """
+ Associate 'levelName' with 'level'.
+
+ This is used when converting levels to text during message formatting.
+ """
+ _acquireLock()
+ try: #unlikely to cause an exception, but you never know...
+ _levelNames[level] = levelName
+ _levelNames[levelName] = level
+ finally:
+ _releaseLock()
+
+def _checkLevel(level):
+ if isinstance(level, (int, long)):
+ rv = level
+ elif str(level) == level:
+ if level not in _levelNames:
+ raise ValueError("Unknown level: %r" % level)
+ rv = _levelNames[level]
+ else:
+ raise TypeError("Level not an integer or a valid string: %r" % level)
+ return rv
+
+#---------------------------------------------------------------------------
+# Thread-related stuff
+#---------------------------------------------------------------------------
+
+#
+#_lock is used to serialize access to shared data structures in this module.
+#This needs to be an RLock because fileConfig() creates and configures
+#Handlers, and so might arbitrary user threads. Since Handler code updates the
+#shared dictionary _handlers, it needs to acquire the lock. But if configuring,
+#the lock would already have been acquired - so we need an RLock.
+#The same argument applies to Loggers and Manager.loggerDict.
+#
+if thread:
+ _lock = threading.RLock()
+else:
+ _lock = None
+
+def _acquireLock():
+ """
+ Acquire the module-level lock for serializing access to shared data.
+
+ This should be released with _releaseLock().
+ """
+ if _lock:
+ _lock.acquire()
+
+def _releaseLock():
+ """
+ Release the module-level lock acquired by calling _acquireLock().
+ """
+ if _lock:
+ _lock.release()
+
+#---------------------------------------------------------------------------
+# The logging record
+#---------------------------------------------------------------------------
+
+class LogRecord(object):
+ """
+ A LogRecord instance represents an event being logged.
+
+ LogRecord instances are created every time something is logged. They
+ contain all the information pertinent to the event being logged. The
+ main information passed in is in msg and args, which are combined
+ using str(msg) % args to create the message field of the record. The
+ record also includes information such as when the record was created,
+ the source line where the logging call was made, and any exception
+ information to be logged.
+ """
+ def __init__(self, name, level, pathname, lineno,
+ msg, args, exc_info, func=None):
+ """
+ Initialize a logging record with interesting information.
+ """
+ ct = time.time()
+ self.name = name
+ self.msg = msg
+ #
+ # The following statement allows passing of a dictionary as a sole
+ # argument, so that you can do something like
+ # logging.debug("a %(a)d b %(b)s", {'a':1, 'b':2})
+ # Suggested by Stefan Behnel.
+ # Note that without the test for args[0], we get a problem because
+ # during formatting, we test to see if the arg is present using
+ # 'if self.args:'. If the event being logged is e.g. 'Value is %d'
+ # and if the passed arg fails 'if self.args:' then no formatting
+ # is done. For example, logger.warn('Value is %d', 0) would log
+ # 'Value is %d' instead of 'Value is 0'.
+ # For the use case of passing a dictionary, this should not be a
+ # problem.
+ if args and len(args) == 1 and isinstance(args[0], dict) and args[0]:
+ args = args[0]
+ self.args = args
+ self.levelname = getLevelName(level)
+ self.levelno = level
+ self.pathname = pathname
+ try:
+ self.filename = os.path.basename(pathname)
+ self.module = os.path.splitext(self.filename)[0]
+ except (TypeError, ValueError, AttributeError):
+ self.filename = pathname
+ self.module = "Unknown module"
+ self.exc_info = exc_info
+ self.exc_text = None # used to cache the traceback text
+ self.lineno = lineno
+ self.funcName = func
+ self.created = ct
+ self.msecs = (ct - long(ct)) * 1000
+ self.relativeCreated = (self.created - _startTime) * 1000
+ if logThreads and thread:
+ self.thread = thread.get_ident()
+ self.threadName = threading.current_thread().name
+ else:
+ self.thread = None
+ self.threadName = None
+ if not logMultiprocessing:
+ self.processName = None
+ else:
+ self.processName = 'MainProcess'
+ mp = sys.modules.get('multiprocessing')
+ if mp is not None:
+ # Errors may occur if multiprocessing has not finished loading
+ # yet - e.g. if a custom import hook causes third-party code
+ # to run when multiprocessing calls import. See issue 8200
+ # for an example
+ try:
+ self.processName = mp.current_process().name
+ except StandardError:
+ pass
+ if logProcesses and hasattr(os, 'getpid'):
+ self.process = os.getpid()
+ else:
+ self.process = None
+
+ def __str__(self):
+ return '<LogRecord: %s, %s, %s, %s, "%s">'%(self.name, self.levelno,
+ self.pathname, self.lineno, self.msg)
+
+ def getMessage(self):
+ """
+ Return the message for this LogRecord.
+
+ Return the message for this LogRecord after merging any user-supplied
+ arguments with the message.
+ """
+ if not _unicode: #if no unicode support...
+ msg = str(self.msg)
+ else:
+ msg = self.msg
+ if not isinstance(msg, basestring):
+ try:
+ msg = str(self.msg)
+ except UnicodeError:
+ msg = self.msg #Defer encoding till later
+ if self.args:
+ msg = msg % self.args
+ return msg
+
+def makeLogRecord(dict):
+ """
+ Make a LogRecord whose attributes are defined by the specified dictionary,
+ This function is useful for converting a logging event received over
+ a socket connection (which is sent as a dictionary) into a LogRecord
+ instance.
+ """
+ rv = LogRecord(None, None, "", 0, "", (), None, None)
+ rv.__dict__.update(dict)
+ return rv
+
+#---------------------------------------------------------------------------
+# Formatter classes and functions
+#---------------------------------------------------------------------------
+
+class Formatter(object):
+ """
+ Formatter instances are used to convert a LogRecord to text.
+
+ Formatters need to know how a LogRecord is constructed. They are
+ responsible for converting a LogRecord to (usually) a string which can
+ be interpreted by either a human or an external system. The base Formatter
+ allows a formatting string to be specified. If none is supplied, the
+ default value of "%s(message)\\n" is used.
+
+ The Formatter can be initialized with a format string which makes use of
+ knowledge of the LogRecord attributes - e.g. the default value mentioned
+ above makes use of the fact that the user's message and arguments are pre-
+ formatted into a LogRecord's message attribute. Currently, the useful
+ attributes in a LogRecord are described by:
+
+ %(name)s Name of the logger (logging channel)
+ %(levelno)s Numeric logging level for the message (DEBUG, INFO,
+ WARNING, ERROR, CRITICAL)
+ %(levelname)s Text logging level for the message ("DEBUG", "INFO",
+ "WARNING", "ERROR", "CRITICAL")
+ %(pathname)s Full pathname of the source file where the logging
+ call was issued (if available)
+ %(filename)s Filename portion of pathname
+ %(module)s Module (name portion of filename)
+ %(lineno)d Source line number where the logging call was issued
+ (if available)
+ %(funcName)s Function name
+ %(created)f Time when the LogRecord was created (time.time()
+ return value)
+ %(asctime)s Textual time when the LogRecord was created
+ %(msecs)d Millisecond portion of the creation time
+ %(relativeCreated)d Time in milliseconds when the LogRecord was created,
+ relative to the time the logging module was loaded
+ (typically at application startup time)
+ %(thread)d Thread ID (if available)
+ %(threadName)s Thread name (if available)
+ %(process)d Process ID (if available)
+ %(message)s The result of record.getMessage(), computed just as
+ the record is emitted
+ """
+
+ converter = time.localtime
+
+ def __init__(self, fmt=None, datefmt=None):
+ """
+ Initialize the formatter with specified format strings.
+
+ Initialize the formatter either with the specified format string, or a
+ default as described above. Allow for specialized date formatting with
+ the optional datefmt argument (if omitted, you get the ISO8601 format).
+ """
+ if fmt:
+ self._fmt = fmt
+ else:
+ self._fmt = "%(message)s"
+ self.datefmt = datefmt
+
+ def formatTime(self, record, datefmt=None):
+ """
+ Return the creation time of the specified LogRecord as formatted text.
+
+ This method should be called from format() by a formatter which
+ wants to make use of a formatted time. This method can be overridden
+ in formatters to provide for any specific requirement, but the
+ basic behaviour is as follows: if datefmt (a string) is specified,
+ it is used with time.strftime() to format the creation time of the
+ record. Otherwise, the ISO8601 format is used. The resulting
+ string is returned. This function uses a user-configurable function
+ to convert the creation time to a tuple. By default, time.localtime()
+ is used; to change this for a particular formatter instance, set the
+ 'converter' attribute to a function with the same signature as
+ time.localtime() or time.gmtime(). To change it for all formatters,
+ for example if you want all logging times to be shown in GMT,
+ set the 'converter' attribute in the Formatter class.
+ """
+ ct = self.converter(record.created)
+ if datefmt:
+ s = time.strftime(datefmt, ct)
+ else:
+ t = time.strftime("%Y-%m-%d %H:%M:%S", ct)
+ s = "%s,%03d" % (t, record.msecs)
+ return s
+
+ def formatException(self, ei):
+ """
+ Format and return the specified exception information as a string.
+
+ This default implementation just uses
+ traceback.print_exception()
+ """
+ sio = cStringIO.StringIO()
+ traceback.print_exception(ei[0], ei[1], ei[2], None, sio)
+ s = sio.getvalue()
+ sio.close()
+ if s[-1:] == "\n":
+ s = s[:-1]
+ return s
+
+ def usesTime(self):
+ """
+ Check if the format uses the creation time of the record.
+ """
+ return self._fmt.find("%(asctime)") >= 0
+
+ def format(self, record):
+ """
+ Format the specified record as text.
+
+ The record's attribute dictionary is used as the operand to a
+ string formatting operation which yields the returned string.
+ Before formatting the dictionary, a couple of preparatory steps
+ are carried out. The message attribute of the record is computed
+ using LogRecord.getMessage(). If the formatting string uses the
+ time (as determined by a call to usesTime(), formatTime() is
+ called to format the event time. If there is exception information,
+ it is formatted using formatException() and appended to the message.
+ """
+ record.message = record.getMessage()
+ if self.usesTime():
+ record.asctime = self.formatTime(record, self.datefmt)
+ s = self._fmt % record.__dict__
+ if record.exc_info:
+ # Cache the traceback text to avoid converting it multiple times
+ # (it's constant anyway)
+ if not record.exc_text:
+ record.exc_text = self.formatException(record.exc_info)
+ if record.exc_text:
+ if s[-1:] != "\n":
+ s = s + "\n"
+ try:
+ s = s + record.exc_text
+ except UnicodeError:
+ # Sometimes filenames have non-ASCII chars, which can lead
+ # to errors when s is Unicode and record.exc_text is str
+ # See issue 8924.
+ # We also use replace for when there are multiple
+ # encodings, e.g. UTF-8 for the filesystem and latin-1
+ # for a script. See issue 13232.
+ s = s + record.exc_text.decode(sys.getfilesystemencoding(),
+ 'replace')
+ return s
+
+#
+# The default formatter to use when no other is specified
+#
+_defaultFormatter = Formatter()
+
+class BufferingFormatter(object):
+ """
+ A formatter suitable for formatting a number of records.
+ """
+ def __init__(self, linefmt=None):
+ """
+ Optionally specify a formatter which will be used to format each
+ individual record.
+ """
+ if linefmt:
+ self.linefmt = linefmt
+ else:
+ self.linefmt = _defaultFormatter
+
+ def formatHeader(self, records):
+ """
+ Return the header string for the specified records.
+ """
+ return ""
+
+ def formatFooter(self, records):
+ """
+ Return the footer string for the specified records.
+ """
+ return ""
+
+ def format(self, records):
+ """
+ Format the specified records and return the result as a string.
+ """
+ rv = ""
+ if len(records) > 0:
+ rv = rv + self.formatHeader(records)
+ for record in records:
+ rv = rv + self.linefmt.format(record)
+ rv = rv + self.formatFooter(records)
+ return rv
+
+#---------------------------------------------------------------------------
+# Filter classes and functions
+#---------------------------------------------------------------------------
+
+class Filter(object):
+ """
+ Filter instances are used to perform arbitrary filtering of LogRecords.
+
+ Loggers and Handlers can optionally use Filter instances to filter
+ records as desired. The base filter class only allows events which are
+ below a certain point in the logger hierarchy. For example, a filter
+ initialized with "A.B" will allow events logged by loggers "A.B",
+ "A.B.C", "A.B.C.D", "A.B.D" etc. but not "A.BB", "B.A.B" etc. If
+ initialized with the empty string, all events are passed.
+ """
+ def __init__(self, name=''):
+ """
+ Initialize a filter.
+
+ Initialize with the name of the logger which, together with its
+ children, will have its events allowed through the filter. If no
+ name is specified, allow every event.
+ """
+ self.name = name
+ self.nlen = len(name)
+
+ def filter(self, record):
+ """
+ Determine if the specified record is to be logged.
+
+ Is the specified record to be logged? Returns 0 for no, nonzero for
+ yes. If deemed appropriate, the record may be modified in-place.
+ """
+ if self.nlen == 0:
+ return 1
+ elif self.name == record.name:
+ return 1
+ elif record.name.find(self.name, 0, self.nlen) != 0:
+ return 0
+ return (record.name[self.nlen] == ".")
+
+class Filterer(object):
+ """
+ A base class for loggers and handlers which allows them to share
+ common code.
+ """
+ def __init__(self):
+ """
+ Initialize the list of filters to be an empty list.
+ """
+ self.filters = []
+
+ def addFilter(self, filter):
+ """
+ Add the specified filter to this handler.
+ """
+ if not (filter in self.filters):
+ self.filters.append(filter)
+
+ def removeFilter(self, filter):
+ """
+ Remove the specified filter from this handler.
+ """
+ if filter in self.filters:
+ self.filters.remove(filter)
+
+ def filter(self, record):
+ """
+ Determine if a record is loggable by consulting all the filters.
+
+ The default is to allow the record to be logged; any filter can veto
+ this and the record is then dropped. Returns a zero value if a record
+ is to be dropped, else non-zero.
+ """
+ rv = 1
+ for f in self.filters:
+ if not f.filter(record):
+ rv = 0
+ break
+ return rv
+
+#---------------------------------------------------------------------------
+# Handler classes and functions
+#---------------------------------------------------------------------------
+
+_handlers = weakref.WeakValueDictionary() #map of handler names to handlers
+_handlerList = [] # added to allow handlers to be removed in reverse of order initialized
+
+def _removeHandlerRef(wr):
+ """
+ Remove a handler reference from the internal cleanup list.
+ """
+ # This function can be called during module teardown, when globals are
+ # set to None. If _acquireLock is None, assume this is the case and do
+ # nothing.
+ if (_acquireLock is not None and _handlerList is not None and
+ _releaseLock is not None):
+ _acquireLock()
+ try:
+ if wr in _handlerList:
+ _handlerList.remove(wr)
+ finally:
+ _releaseLock()
+
+def _addHandlerRef(handler):
+ """
+ Add a handler to the internal cleanup list using a weak reference.
+ """
+ _acquireLock()
+ try:
+ _handlerList.append(weakref.ref(handler, _removeHandlerRef))
+ finally:
+ _releaseLock()
+
+class Handler(Filterer):
+ """
+ Handler instances dispatch logging events to specific destinations.
+
+ The base handler class. Acts as a placeholder which defines the Handler
+ interface. Handlers can optionally use Formatter instances to format
+ records as desired. By default, no formatter is specified; in this case,
+ the 'raw' message as determined by record.message is logged.
+ """
+ def __init__(self, level=NOTSET):
+ """
+ Initializes the instance - basically setting the formatter to None
+ and the filter list to empty.
+ """
+ Filterer.__init__(self)
+ self._name = None
+ self.level = _checkLevel(level)
+ self.formatter = None
+ # Add the handler to the global _handlerList (for cleanup on shutdown)
+ _addHandlerRef(self)
+ self.createLock()
+
+ def get_name(self):
+ return self._name
+
+ def set_name(self, name):
+ _acquireLock()
+ try:
+ if self._name in _handlers:
+ del _handlers[self._name]
+ self._name = name
+ if name:
+ _handlers[name] = self
+ finally:
+ _releaseLock()
+
+ name = property(get_name, set_name)
+
+ def createLock(self):
+ """
+ Acquire a thread lock for serializing access to the underlying I/O.
+ """
+ if thread:
+ self.lock = threading.RLock()
+ else:
+ self.lock = None
+
+ def acquire(self):
+ """
+ Acquire the I/O thread lock.
+ """
+ if self.lock:
+ self.lock.acquire()
+
+ def release(self):
+ """
+ Release the I/O thread lock.
+ """
+ if self.lock:
+ self.lock.release()
+
+ def setLevel(self, level):
+ """
+ Set the logging level of this handler.
+ """
+ self.level = _checkLevel(level)
+
+ def format(self, record):
+ """
+ Format the specified record.
+
+ If a formatter is set, use it. Otherwise, use the default formatter
+ for the module.
+ """
+ if self.formatter:
+ fmt = self.formatter
+ else:
+ fmt = _defaultFormatter
+ return fmt.format(record)
+
+ def emit(self, record):
+ """
+ Do whatever it takes to actually log the specified logging record.
+
+ This version is intended to be implemented by subclasses and so
+ raises a NotImplementedError.
+ """
+ raise NotImplementedError('emit must be implemented '
+ 'by Handler subclasses')
+
+ def handle(self, record):
+ """
+ Conditionally emit the specified logging record.
+
+ Emission depends on filters which may have been added to the handler.
+ Wrap the actual emission of the record with acquisition/release of
+ the I/O thread lock. Returns whether the filter passed the record for
+ emission.
+ """
+ rv = self.filter(record)
+ if rv:
+ self.acquire()
+ try:
+ self.emit(record)
+ finally:
+ self.release()
+ return rv
+
+ def setFormatter(self, fmt):
+ """
+ Set the formatter for this handler.
+ """
+ self.formatter = fmt
+
+ def flush(self):
+ """
+ Ensure all logging output has been flushed.
+
+ This version does nothing and is intended to be implemented by
+ subclasses.
+ """
+ pass
+
+ def close(self):
+ """
+ Tidy up any resources used by the handler.
+
+ This version removes the handler from an internal map of handlers,
+ _handlers, which is used for handler lookup by name. Subclasses
+ should ensure that this gets called from overridden close()
+ methods.
+ """
+ #get the module data lock, as we're updating a shared structure.
+ _acquireLock()
+ try: #unlikely to raise an exception, but you never know...
+ if self._name and self._name in _handlers:
+ del _handlers[self._name]
+ finally:
+ _releaseLock()
+
+ def handleError(self, record):
+ """
+ Handle errors which occur during an emit() call.
+
+ This method should be called from handlers when an exception is
+ encountered during an emit() call. If raiseExceptions is false,
+ exceptions get silently ignored. This is what is mostly wanted
+ for a logging system - most users will not care about errors in
+ the logging system, they are more interested in application errors.
+ You could, however, replace this with a custom handler if you wish.
+ The record which was being processed is passed in to this method.
+ """
+ if raiseExceptions and sys.stderr: # see issue 13807
+ ei = sys.exc_info()
+ try:
+ traceback.print_exception(ei[0], ei[1], ei[2],
+ None, sys.stderr)
+ sys.stderr.write('Logged from file %s, line %s\n' % (
+ record.filename, record.lineno))
+ except IOError:
+ pass # see issue 5971
+ finally:
+ del ei
+
+class StreamHandler(Handler):
+ """
+ A handler class which writes logging records, appropriately formatted,
+ to a stream. Note that this class does not close the stream, as
+ sys.stdout or sys.stderr may be used.
+ """
+
+ def __init__(self, stream=None):
+ """
+ Initialize the handler.
+
+ If stream is not specified, sys.stderr is used.
+ """
+ Handler.__init__(self)
+ if stream is None:
+ stream = sys.stderr
+ self.stream = stream
+
+ def flush(self):
+ """
+ Flushes the stream.
+ """
+ self.acquire()
+ try:
+ if self.stream and hasattr(self.stream, "flush"):
+ self.stream.flush()
+ finally:
+ self.release()
+
+ def emit(self, record):
+ """
+ Emit a record.
+
+ If a formatter is specified, it is used to format the record.
+ The record is then written to the stream with a trailing newline. If
+ exception information is present, it is formatted using
+ traceback.print_exception and appended to the stream. If the stream
+ has an 'encoding' attribute, it is used to determine how to do the
+ output to the stream.
+ """
+ try:
+ msg = self.format(record)
+ stream = self.stream
+ fs = "%s\n"
+ if not _unicode: #if no unicode support...
+ stream.write(fs % msg)
+ else:
+ try:
+ if (isinstance(msg, unicode) and
+ getattr(stream, 'encoding', None)):
+ ufs = fs.decode(stream.encoding)
+ try:
+ stream.write(ufs % msg)
+ except UnicodeEncodeError:
+ #Printing to terminals sometimes fails. For example,
+ #with an encoding of 'cp1251', the above write will
+ #work if written to a stream opened or wrapped by
+ #the codecs module, but fail when writing to a
+ #terminal even when the codepage is set to cp1251.
+ #An extra encoding step seems to be needed.
+ stream.write((ufs % msg).encode(stream.encoding))
+ else:
+ stream.write(fs % msg)
+ except UnicodeError:
+ stream.write(fs % msg.encode("UTF-8"))
+ self.flush()
+ except (KeyboardInterrupt, SystemExit):
+ raise
+ except:
+ self.handleError(record)
+
+class FileHandler(StreamHandler):
+ """
+ A handler class which writes formatted logging records to disk files.
+ """
+ def __init__(self, filename, mode='a', encoding=None, delay=0):
+ """
+ Open the specified file and use it as the stream for logging.
+ """
+ #keep the absolute path, otherwise derived classes which use this
+ #may come a cropper when the current directory changes
+ if codecs is None:
+ encoding = None
+ self.baseFilename = os.path.abspath(filename)
+ self.mode = mode
+ self.encoding = encoding
+ if delay:
+ #We don't open the stream, but we still need to call the
+ #Handler constructor to set level, formatter, lock etc.
+ Handler.__init__(self)
+ self.stream = None
+ else:
+ StreamHandler.__init__(self, self._open())
+
+ def close(self):
+ """
+ Closes the stream.
+ """
+ self.acquire()
+ try:
+ if self.stream:
+ self.flush()
+ if hasattr(self.stream, "close"):
+ self.stream.close()
+ StreamHandler.close(self)
+ self.stream = None
+ finally:
+ self.release()
+
+ def _open(self):
+ """
+ Open the current base file with the (original) mode and encoding.
+ Return the resulting stream.
+ """
+ if self.encoding is None:
+ stream = open(self.baseFilename, self.mode)
+ else:
+ stream = codecs.open(self.baseFilename, self.mode, self.encoding)
+ return stream
+
+ def emit(self, record):
+ """
+ Emit a record.
+
+ If the stream was not opened because 'delay' was specified in the
+ constructor, open it before calling the superclass's emit.
+ """
+ if self.stream is None:
+ self.stream = self._open()
+ StreamHandler.emit(self, record)
+
+#---------------------------------------------------------------------------
+# Manager classes and functions
+#---------------------------------------------------------------------------
+
+class PlaceHolder(object):
+ """
+ PlaceHolder instances are used in the Manager logger hierarchy to take
+ the place of nodes for which no loggers have been defined. This class is
+ intended for internal use only and not as part of the public API.
+ """
+ def __init__(self, alogger):
+ """
+ Initialize with the specified logger being a child of this placeholder.
+ """
+ #self.loggers = [alogger]
+ self.loggerMap = { alogger : None }
+
+ def append(self, alogger):
+ """
+ Add the specified logger as a child of this placeholder.
+ """
+ #if alogger not in self.loggers:
+ if alogger not in self.loggerMap:
+ #self.loggers.append(alogger)
+ self.loggerMap[alogger] = None
+
+#
+# Determine which class to use when instantiating loggers.
+#
+_loggerClass = None
+
+def setLoggerClass(klass):
+ """
+ Set the class to be used when instantiating a logger. The class should
+ define __init__() such that only a name argument is required, and the
+ __init__() should call Logger.__init__()
+ """
+ if klass != Logger:
+ if not issubclass(klass, Logger):
+ raise TypeError("logger not derived from logging.Logger: "
+ + klass.__name__)
+ global _loggerClass
+ _loggerClass = klass
+
+def getLoggerClass():
+ """
+ Return the class to be used when instantiating a logger.
+ """
+
+ return _loggerClass
+
+class Manager(object):
+ """
+ There is [under normal circumstances] just one Manager instance, which
+ holds the hierarchy of loggers.
+ """
+ def __init__(self, rootnode):
+ """
+ Initialize the manager with the root node of the logger hierarchy.
+ """
+ self.root = rootnode
+ self.disable = 0
+ self.emittedNoHandlerWarning = 0
+ self.loggerDict = {}
+ self.loggerClass = None
+
+ def getLogger(self, name):
+ """
+ Get a logger with the specified name (channel name), creating it
+ if it doesn't yet exist. This name is a dot-separated hierarchical
+ name, such as "a", "a.b", "a.b.c" or similar.
+
+ If a PlaceHolder existed for the specified name [i.e. the logger
+ didn't exist but a child of it did], replace it with the created
+ logger and fix up the parent/child references which pointed to the
+ placeholder to now point to the logger.
+ """
+ rv = None
+ if not isinstance(name, basestring):
+ raise TypeError('A logger name must be string or Unicode')
+ if isinstance(name, unicode):
+ name = name.encode('utf-8')
+ _acquireLock()
+ try:
+ if name in self.loggerDict:
+ rv = self.loggerDict[name]
+ if isinstance(rv, PlaceHolder):
+ ph = rv
+ rv = (self.loggerClass or _loggerClass)(name)
+ rv.manager = self
+ self.loggerDict[name] = rv
+ self._fixupChildren(ph, rv)
+ self._fixupParents(rv)
+ else:
+ rv = (self.loggerClass or _loggerClass)(name)
+ rv.manager = self
+ self.loggerDict[name] = rv
+ self._fixupParents(rv)
+ finally:
+ _releaseLock()
+ return rv
+
+ def setLoggerClass(self, klass):
+ """
+ Set the class to be used when instantiating a logger with this Manager.
+ """
+ if klass != Logger:
+ if not issubclass(klass, Logger):
+ raise TypeError("logger not derived from logging.Logger: "
+ + klass.__name__)
+ self.loggerClass = klass
+
+ def _fixupParents(self, alogger):
+ """
+ Ensure that there are either loggers or placeholders all the way
+ from the specified logger to the root of the logger hierarchy.
+ """
+ name = alogger.name
+ i = name.rfind(".")
+ rv = None
+ while (i > 0) and not rv:
+ substr = name[:i]
+ if substr not in self.loggerDict:
+ self.loggerDict[substr] = PlaceHolder(alogger)
+ else:
+ obj = self.loggerDict[substr]
+ if isinstance(obj, Logger):
+ rv = obj
+ else:
+ assert isinstance(obj, PlaceHolder)
+ obj.append(alogger)
+ i = name.rfind(".", 0, i - 1)
+ if not rv:
+ rv = self.root
+ alogger.parent = rv
+
+ def _fixupChildren(self, ph, alogger):
+ """
+ Ensure that children of the placeholder ph are connected to the
+ specified logger.
+ """
+ name = alogger.name
+ namelen = len(name)
+ for c in ph.loggerMap.keys():
+ #The if means ... if not c.parent.name.startswith(nm)
+ if c.parent.name[:namelen] != name:
+ alogger.parent = c.parent
+ c.parent = alogger
+
+#---------------------------------------------------------------------------
+# Logger classes and functions
+#---------------------------------------------------------------------------
+
+class Logger(Filterer):
+ """
+ Instances of the Logger class represent a single logging channel. A
+ "logging channel" indicates an area of an application. Exactly how an
+ "area" is defined is up to the application developer. Since an
+ application can have any number of areas, logging channels are identified
+ by a unique string. Application areas can be nested (e.g. an area
+ of "input processing" might include sub-areas "read CSV files", "read
+ XLS files" and "read Gnumeric files"). To cater for this natural nesting,
+ channel names are organized into a namespace hierarchy where levels are
+ separated by periods, much like the Java or Python package namespace. So
+ in the instance given above, channel names might be "input" for the upper
+ level, and "input.csv", "input.xls" and "input.gnu" for the sub-levels.
+ There is no arbitrary limit to the depth of nesting.
+ """
+ def __init__(self, name, level=NOTSET):
+ """
+ Initialize the logger with a name and an optional level.
+ """
+ Filterer.__init__(self)
+ self.name = name
+ self.level = _checkLevel(level)
+ self.parent = None
+ self.propagate = 1
+ self.handlers = []
+ self.disabled = 0
+
+ def setLevel(self, level):
+ """
+ Set the logging level of this logger.
+ """
+ self.level = _checkLevel(level)
+
+ def debug(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'DEBUG'.
+
+ To pass exception information, use the keyword argument exc_info with
+ a true value, e.g.
+
+ logger.debug("Houston, we have a %s", "thorny problem", exc_info=1)
+ """
+ if self.isEnabledFor(DEBUG):
+ self._log(DEBUG, msg, args, **kwargs)
+
+ def info(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'INFO'.
+
+ To pass exception information, use the keyword argument exc_info with
+ a true value, e.g.
+
+ logger.info("Houston, we have a %s", "interesting problem", exc_info=1)
+ """
+ if self.isEnabledFor(INFO):
+ self._log(INFO, msg, args, **kwargs)
+
+ def warning(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'WARNING'.
+
+ To pass exception information, use the keyword argument exc_info with
+ a true value, e.g.
+
+ logger.warning("Houston, we have a %s", "bit of a problem", exc_info=1)
+ """
+ if self.isEnabledFor(WARNING):
+ self._log(WARNING, msg, args, **kwargs)
+
+ warn = warning
+
+ def error(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'ERROR'.
+
+ To pass exception information, use the keyword argument exc_info with
+ a true value, e.g.
+
+ logger.error("Houston, we have a %s", "major problem", exc_info=1)
+ """
+ if self.isEnabledFor(ERROR):
+ self._log(ERROR, msg, args, **kwargs)
+
+ def exception(self, msg, *args, **kwargs):
+ """
+ Convenience method for logging an ERROR with exception information.
+ """
+ kwargs['exc_info'] = 1
+ self.error(msg, *args, **kwargs)
+
+ def critical(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'CRITICAL'.
+
+ To pass exception information, use the keyword argument exc_info with
+ a true value, e.g.
+
+ logger.critical("Houston, we have a %s", "major disaster", exc_info=1)
+ """
+ if self.isEnabledFor(CRITICAL):
+ self._log(CRITICAL, msg, args, **kwargs)
+
+ fatal = critical
+
+ def log(self, level, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with the integer severity 'level'.
+
+ To pass exception information, use the keyword argument exc_info with
+ a true value, e.g.
+
+ logger.log(level, "We have a %s", "mysterious problem", exc_info=1)
+ """
+ if not isinstance(level, int):
+ if raiseExceptions:
+ raise TypeError("level must be an integer")
+ else:
+ return
+ if self.isEnabledFor(level):
+ self._log(level, msg, args, **kwargs)
+
+ def findCaller(self):
+ """
+ Find the stack frame of the caller so that we can note the source
+ file name, line number and function name.
+ """
+ f = currentframe()
+ #On some versions of IronPython, currentframe() returns None if
+ #IronPython isn't run with -X:Frames.
+ if f is not None:
+ f = f.f_back
+ rv = "(unknown file)", 0, "(unknown function)"
+ while hasattr(f, "f_code"):
+ co = f.f_code
+ filename = os.path.normcase(co.co_filename)
+ if filename == _srcfile:
+ f = f.f_back
+ continue
+ rv = (co.co_filename, f.f_lineno, co.co_name)
+ break
+ return rv
+
+ def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
+ """
+ A factory method which can be overridden in subclasses to create
+ specialized LogRecords.
+ """
+ rv = LogRecord(name, level, fn, lno, msg, args, exc_info, func)
+ if extra is not None:
+ for key in extra:
+ if (key in ["message", "asctime"]) or (key in rv.__dict__):
+ raise KeyError("Attempt to overwrite %r in LogRecord" % key)
+ rv.__dict__[key] = extra[key]
+ return rv
+
+ def _log(self, level, msg, args, exc_info=None, extra=None):
+ """
+ Low-level logging routine which creates a LogRecord and then calls
+ all the handlers of this logger to handle the record.
+ """
+ if _srcfile:
+ #IronPython doesn't track Python frames, so findCaller raises an
+ #exception on some versions of IronPython. We trap it here so that
+ #IronPython can use logging.
+ try:
+ fn, lno, func = self.findCaller()
+ except ValueError:
+ fn, lno, func = "(unknown file)", 0, "(unknown function)"
+ else:
+ fn, lno, func = "(unknown file)", 0, "(unknown function)"
+ if exc_info:
+ if not isinstance(exc_info, tuple):
+ exc_info = sys.exc_info()
+ record = self.makeRecord(self.name, level, fn, lno, msg, args, exc_info, func, extra)
+ self.handle(record)
+
+ def handle(self, record):
+ """
+ Call the handlers for the specified record.
+
+ This method is used for unpickled records received from a socket, as
+ well as those created locally. Logger-level filtering is applied.
+ """
+ if (not self.disabled) and self.filter(record):
+ self.callHandlers(record)
+
+ def addHandler(self, hdlr):
+ """
+ Add the specified handler to this logger.
+ """
+ _acquireLock()
+ try:
+ if not (hdlr in self.handlers):
+ self.handlers.append(hdlr)
+ finally:
+ _releaseLock()
+
+ def removeHandler(self, hdlr):
+ """
+ Remove the specified handler from this logger.
+ """
+ _acquireLock()
+ try:
+ if hdlr in self.handlers:
+ self.handlers.remove(hdlr)
+ finally:
+ _releaseLock()
+
+ def callHandlers(self, record):
+ """
+ Pass a record to all relevant handlers.
+
+ Loop through all handlers for this logger and its parents in the
+ logger hierarchy. If no handler was found, output a one-off error
+ message to sys.stderr. Stop searching up the hierarchy whenever a
+ logger with the "propagate" attribute set to zero is found - that
+ will be the last logger whose handlers are called.
+ """
+ c = self
+ found = 0
+ while c:
+ for hdlr in c.handlers:
+ found = found + 1
+ if record.levelno >= hdlr.level:
+ hdlr.handle(record)
+ if not c.propagate:
+ c = None #break out
+ else:
+ c = c.parent
+ if (found == 0) and raiseExceptions and not self.manager.emittedNoHandlerWarning:
+ sys.stderr.write("No handlers could be found for logger"
+ " \"%s\"\n" % self.name)
+ self.manager.emittedNoHandlerWarning = 1
+
+ def getEffectiveLevel(self):
+ """
+ Get the effective level for this logger.
+
+ Loop through this logger and its parents in the logger hierarchy,
+ looking for a non-zero logging level. Return the first one found.
+ """
+ logger = self
+ while logger:
+ if logger.level:
+ return logger.level
+ logger = logger.parent
+ return NOTSET
+
+ def isEnabledFor(self, level):
+ """
+ Is this logger enabled for level 'level'?
+ """
+ if self.manager.disable >= level:
+ return 0
+ return level >= self.getEffectiveLevel()
+
+ def getChild(self, suffix):
+ """
+ Get a logger which is a descendant to this one.
+
+ This is a convenience method, such that
+
+ logging.getLogger('abc').getChild('def.ghi')
+
+ is the same as
+
+ logging.getLogger('abc.def.ghi')
+
+ It's useful, for example, when the parent logger is named using
+ __name__ rather than a literal string.
+ """
+ if self.root is not self:
+ suffix = '.'.join((self.name, suffix))
+ return self.manager.getLogger(suffix)
+
+class RootLogger(Logger):
+ """
+ A root logger is not that different to any other logger, except that
+ it must have a logging level and there is only one instance of it in
+ the hierarchy.
+ """
+ def __init__(self, level):
+ """
+ Initialize the logger with the name "root".
+ """
+ Logger.__init__(self, "root", level)
+
+_loggerClass = Logger
+
+class LoggerAdapter(object):
+ """
+ An adapter for loggers which makes it easier to specify contextual
+ information in logging output.
+ """
+
+ def __init__(self, logger, extra):
+ """
+ Initialize the adapter with a logger and a dict-like object which
+ provides contextual information. This constructor signature allows
+ easy stacking of LoggerAdapters, if so desired.
+
+ You can effectively pass keyword arguments as shown in the
+ following example:
+
+ adapter = LoggerAdapter(someLogger, dict(p1=v1, p2="v2"))
+ """
+ self.logger = logger
+ self.extra = extra
+
+ def process(self, msg, kwargs):
+ """
+ Process the logging message and keyword arguments passed in to
+ a logging call to insert contextual information. You can either
+ manipulate the message itself, the keyword args or both. Return
+ the message and kwargs modified (or not) to suit your needs.
+
+ Normally, you'll only need to override this one method in a
+ LoggerAdapter subclass for your specific needs.
+ """
+ kwargs["extra"] = self.extra
+ return msg, kwargs
+
+ def debug(self, msg, *args, **kwargs):
+ """
+ Delegate a debug call to the underlying logger, after adding
+ contextual information from this adapter instance.
+ """
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.debug(msg, *args, **kwargs)
+
+ def info(self, msg, *args, **kwargs):
+ """
+ Delegate an info call to the underlying logger, after adding
+ contextual information from this adapter instance.
+ """
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.info(msg, *args, **kwargs)
+
+ def warning(self, msg, *args, **kwargs):
+ """
+ Delegate a warning call to the underlying logger, after adding
+ contextual information from this adapter instance.
+ """
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.warning(msg, *args, **kwargs)
+
+ def error(self, msg, *args, **kwargs):
+ """
+ Delegate an error call to the underlying logger, after adding
+ contextual information from this adapter instance.
+ """
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.error(msg, *args, **kwargs)
+
+ def exception(self, msg, *args, **kwargs):
+ """
+ Delegate an exception call to the underlying logger, after adding
+ contextual information from this adapter instance.
+ """
+ msg, kwargs = self.process(msg, kwargs)
+ kwargs["exc_info"] = 1
+ self.logger.error(msg, *args, **kwargs)
+
+ def critical(self, msg, *args, **kwargs):
+ """
+ Delegate a critical call to the underlying logger, after adding
+ contextual information from this adapter instance.
+ """
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.critical(msg, *args, **kwargs)
+
+ def log(self, level, msg, *args, **kwargs):
+ """
+ Delegate a log call to the underlying logger, after adding
+ contextual information from this adapter instance.
+ """
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.log(level, msg, *args, **kwargs)
+
+ def isEnabledFor(self, level):
+ """
+ See if the underlying logger is enabled for the specified level.
+ """
+ return self.logger.isEnabledFor(level)
+
+root = RootLogger(WARNING)
+Logger.root = root
+Logger.manager = Manager(Logger.root)
+
+#---------------------------------------------------------------------------
+# Configuration classes and functions
+#---------------------------------------------------------------------------
+
+BASIC_FORMAT = "%(levelname)s:%(name)s:%(message)s"
+
+def basicConfig(**kwargs):
+ """
+ Do basic configuration for the logging system.
+
+ This function does nothing if the root logger already has handlers
+ configured. It is a convenience method intended for use by simple scripts
+ to do one-shot configuration of the logging package.
+
+ The default behaviour is to create a StreamHandler which writes to
+ sys.stderr, set a formatter using the BASIC_FORMAT format string, and
+ add the handler to the root logger.
+
+ A number of optional keyword arguments may be specified, which can alter
+ the default behaviour.
+
+ filename Specifies that a FileHandler be created, using the specified
+ filename, rather than a StreamHandler.
+ filemode Specifies the mode to open the file, if filename is specified
+ (if filemode is unspecified, it defaults to 'a').
+ format Use the specified format string for the handler.
+ datefmt Use the specified date/time format.
+ level Set the root logger level to the specified level.
+ stream Use the specified stream to initialize the StreamHandler. Note
+ that this argument is incompatible with 'filename' - if both
+ are present, 'stream' is ignored.
+
+ Note that you could specify a stream created using open(filename, mode)
+ rather than passing the filename and mode in. However, it should be
+ remembered that StreamHandler does not close its stream (since it may be
+ using sys.stdout or sys.stderr), whereas FileHandler closes its stream
+ when the handler is closed.
+ """
+ # Add thread safety in case someone mistakenly calls
+ # basicConfig() from multiple threads
+ _acquireLock()
+ try:
+ if len(root.handlers) == 0:
+ filename = kwargs.get("filename")
+ if filename:
+ mode = kwargs.get("filemode", 'a')
+ hdlr = FileHandler(filename, mode)
+ else:
+ stream = kwargs.get("stream")
+ hdlr = StreamHandler(stream)
+ fs = kwargs.get("format", BASIC_FORMAT)
+ dfs = kwargs.get("datefmt", None)
+ fmt = Formatter(fs, dfs)
+ hdlr.setFormatter(fmt)
+ root.addHandler(hdlr)
+ level = kwargs.get("level")
+ if level is not None:
+ root.setLevel(level)
+ finally:
+ _releaseLock()
+
+#---------------------------------------------------------------------------
+# Utility functions at module level.
+# Basically delegate everything to the root logger.
+#---------------------------------------------------------------------------
+
+def getLogger(name=None):
+ """
+ Return a logger with the specified name, creating it if necessary.
+
+ If no name is specified, return the root logger.
+ """
+ if name:
+ return Logger.manager.getLogger(name)
+ else:
+ return root
+
+#def getRootLogger():
+# """
+# Return the root logger.
+#
+# Note that getLogger('') now does the same thing, so this function is
+# deprecated and may disappear in the future.
+# """
+# return root
+
+def critical(msg, *args, **kwargs):
+ """
+ Log a message with severity 'CRITICAL' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ root.critical(msg, *args, **kwargs)
+
+fatal = critical
+
+def error(msg, *args, **kwargs):
+ """
+ Log a message with severity 'ERROR' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ root.error(msg, *args, **kwargs)
+
+def exception(msg, *args, **kwargs):
+ """
+ Log a message with severity 'ERROR' on the root logger,
+ with exception information.
+ """
+ kwargs['exc_info'] = 1
+ error(msg, *args, **kwargs)
+
+def warning(msg, *args, **kwargs):
+ """
+ Log a message with severity 'WARNING' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ root.warning(msg, *args, **kwargs)
+
+warn = warning
+
+def info(msg, *args, **kwargs):
+ """
+ Log a message with severity 'INFO' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ 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()
+ root.debug(msg, *args, **kwargs)
+
+def log(level, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with the integer severity 'level' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ root.log(level, msg, *args, **kwargs)
+
+def disable(level):
+ """
+ Disable all logging calls of severity 'level' and below.
+ """
+ root.manager.disable = level
+
+def shutdown(handlerList=_handlerList):
+ """
+ Perform any cleanup actions in the logging system (e.g. flushing
+ buffers).
+
+ Should be called at application exit.
+ """
+ for wr in reversed(handlerList[:]):
+ #errors might occur, for example, if files are locked
+ #we just ignore them if raiseExceptions is not set
+ try:
+ h = wr()
+ if h:
+ try:
+ h.acquire()
+ h.flush()
+ h.close()
+ except (IOError, ValueError):
+ # Ignore errors which might be caused
+ # because handlers have been closed but
+ # references to them are still around at
+ # application exit.
+ pass
+ finally:
+ h.release()
+ except:
+ if raiseExceptions:
+ raise
+ #else, swallow
+
+#Let's try and shutdown automatically on application exit...
+import atexit
+atexit.register(shutdown)
+
+# Null handler
+
+class NullHandler(Handler):
+ """
+ This handler does nothing. It's intended to be used to avoid the
+ "No handlers could be found for logger XXX" one-off warning. This is
+ important for library code, which may contain code to log events. If a user
+ of the library does not configure logging, the one-off warning might be
+ produced; to avoid this, the library developer simply needs to instantiate
+ a NullHandler and add it to the top-level logger of the library module or
+ package.
+ """
+ def handle(self, record):
+ pass
+
+ def emit(self, record):
+ pass
+
+ def createLock(self):
+ self.lock = None
+
+# Warnings integration
+
+_warnings_showwarning = None
+
+def _showwarning(message, category, filename, lineno, file=None, line=None):
+ """
+ Implementation of showwarnings which redirects to logging, which will first
+ check to see if the file parameter is None. If a file is specified, it will
+ delegate to the original warnings implementation of showwarning. Otherwise,
+ it will call warnings.formatwarning and will log the resulting string to a
+ warnings logger named "py.warnings" with level logging.WARNING.
+ """
+ if file is not None:
+ if _warnings_showwarning is not None:
+ _warnings_showwarning(message, category, filename, lineno, file, line)
+ else:
+ s = warnings.formatwarning(message, category, filename, lineno, line)
+ logger = getLogger("py.warnings")
+ if not logger.handlers:
+ logger.addHandler(NullHandler())
+ logger.warning("%s", s)
+
+def captureWarnings(capture):
+ """
+ If capture is true, redirect all warnings to the logging package.
+ If capture is False, ensure that warnings are not redirected to logging
+ but to their original destinations.
+ """
+ global _warnings_showwarning
+ if capture:
+ if _warnings_showwarning is None:
+ _warnings_showwarning = warnings.showwarning
+ warnings.showwarning = _showwarning
+ else:
+ if _warnings_showwarning is not None:
+ warnings.showwarning = _warnings_showwarning
+ _warnings_showwarning = None