[Python-checkins] r87157 - in python/branches/py3k: Lib/logging/__init__.py Lib/test/test_logging.py Misc/NEWS

vinay.sajip python-checkins at python.org
Fri Dec 10 12:42:57 CET 2010


Author: vinay.sajip
Date: Fri Dec 10 12:42:57 2010
New Revision: 87157

Log:
logging: added handler of last resort.

Modified:
   python/branches/py3k/Lib/logging/__init__.py
   python/branches/py3k/Lib/test/test_logging.py
   python/branches/py3k/Misc/NEWS

Modified: python/branches/py3k/Lib/logging/__init__.py
==============================================================================
--- python/branches/py3k/Lib/logging/__init__.py	(original)
+++ python/branches/py3k/Lib/logging/__init__.py	Fri Dec 10 12:42:57 2010
@@ -33,7 +33,7 @@
            'captureWarnings', 'critical', 'debug', 'disable', 'error',
            'exception', 'fatal', 'getLevelName', 'getLogger', 'getLoggerClass',
            'info', 'log', 'makeLogRecord', 'setLoggerClass', 'warn', 'warning',
-           'getLogRecordFactory', 'setLogRecordFactory']
+           'getLogRecordFactory', 'setLogRecordFactory', 'lastResort']
 
 try:
     import codecs
@@ -997,6 +997,26 @@
             self.stream = self._open()
         StreamHandler.emit(self, record)
 
+class _StderrHandler(StreamHandler):
+    """
+    This class is like a StreamHandler using sys.stderr, but always uses
+    whatever sys.stderr is currently set to rather than the value of
+    sys.stderr at handler construction time.
+    """
+    def __init__(self, level=NOTSET):
+        """
+        Initialize the handler.
+        """
+        Handler.__init__(self, level)
+
+    @property
+    def stream(self):
+        return sys.stderr
+
+
+_defaultLastResort = _StderrHandler(WARNING)
+lastResort = _defaultLastResort
+
 #---------------------------------------------------------------------------
 #   Manager classes and functions
 #---------------------------------------------------------------------------
@@ -1056,7 +1076,7 @@
         """
         self.root = rootnode
         self.disable = 0
-        self.emittedNoHandlerWarning = 0
+        self.emittedNoHandlerWarning = False
         self.loggerDict = {}
         self.loggerClass = None
         self.logRecordFactory = None
@@ -1415,10 +1435,13 @@
                 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
+        if (found == 0):
+            if lastResort:
+                lastResort.handle(record)
+            elif raiseExceptions and not self.manager.emittedNoHandlerWarning:
+                sys.stderr.write("No handlers could be found for logger"
+                                 " \"%s\"\n" % self.name)
+                self.manager.emittedNoHandlerWarning = True
 
     def getEffectiveLevel(self):
         """
@@ -1676,7 +1699,9 @@
 
 def critical(msg, *args, **kwargs):
     """
-    Log a message with severity 'CRITICAL' on the root logger.
+    Log a message with severity 'CRITICAL' on the root logger. If the logger
+    has no handlers, call basicConfig() to add a console handler with a
+    pre-defined format.
     """
     if len(root.handlers) == 0:
         basicConfig()
@@ -1686,7 +1711,9 @@
 
 def error(msg, *args, **kwargs):
     """
-    Log a message with severity 'ERROR' on the root logger.
+    Log a message with severity 'ERROR' on the root logger. If the logger has
+    no handlers, call basicConfig() to add a console handler with a pre-defined
+    format.
     """
     if len(root.handlers) == 0:
         basicConfig()
@@ -1694,15 +1721,18 @@
 
 def exception(msg, *args, **kwargs):
     """
-    Log a message with severity 'ERROR' on the root logger,
-    with exception information.
+    Log a message with severity 'ERROR' on the root logger, with exception
+    information. If the logger has no handlers, basicConfig() is called to add
+    a console handler with a pre-defined format.
     """
     kwargs['exc_info'] = True
     error(msg, *args, **kwargs)
 
 def warning(msg, *args, **kwargs):
     """
-    Log a message with severity 'WARNING' on the root logger.
+    Log a message with severity 'WARNING' on the root logger. If the logger has
+    no handlers, call basicConfig() to add a console handler with a pre-defined
+    format.
     """
     if len(root.handlers) == 0:
         basicConfig()
@@ -1712,7 +1742,9 @@
 
 def info(msg, *args, **kwargs):
     """
-    Log a message with severity 'INFO' on the root logger.
+    Log a message with severity 'INFO' on the root logger. If the logger has
+    no handlers, call basicConfig() to add a console handler with a pre-defined
+    format.
     """
     if len(root.handlers) == 0:
         basicConfig()
@@ -1720,7 +1752,9 @@
 
 def debug(msg, *args, **kwargs):
     """
-    Log a message with severity 'DEBUG' on the root logger.
+    Log a message with severity 'DEBUG' on the root logger. If the logger has
+    no handlers, call basicConfig() to add a console handler with a pre-defined
+    format.
     """
     if len(root.handlers) == 0:
         basicConfig()
@@ -1728,7 +1762,9 @@
 
 def log(level, msg, *args, **kwargs):
     """
-    Log 'msg % args' with the integer severity 'level' on the root logger.
+    Log 'msg % args' with the integer severity 'level' on the root logger. If
+    the logger has no handlers, call basicConfig() to add a console handler
+    with a pre-defined format.
     """
     if len(root.handlers) == 0:
         basicConfig()

Modified: python/branches/py3k/Lib/test/test_logging.py
==============================================================================
--- python/branches/py3k/Lib/test/test_logging.py	(original)
+++ python/branches/py3k/Lib/test/test_logging.py	Fri Dec 10 12:42:57 2010
@@ -1927,6 +1927,40 @@
         f = logging.Formatter('asctime', style='$')
         self.assertFalse(f.usesTime())
 
+class LastResortTest(BaseTest):
+    def test_last_resort(self):
+        "Test the last resort handler"
+        root = self.root_logger
+        root.removeHandler(self.root_hdlr)
+        old_stderr = sys.stderr
+        old_lastresort = logging.lastResort
+        old_raise_exceptions = logging.raiseExceptions
+        try:
+            sys.stderr = sio = io.StringIO()
+            root.warning('This is your final chance!')
+            self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
+            #No handlers and no last resort, so 'No handlers' message
+            logging.lastResort = None
+            sys.stderr = sio = io.StringIO()
+            root.warning('This is your final chance!')
+            self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
+            # 'No handlers' message only printed once
+            sys.stderr = sio = io.StringIO()
+            root.warning('This is your final chance!')
+            self.assertEqual(sio.getvalue(), '')
+            root.manager.emittedNoHandlerWarning = False
+            #If raiseExceptions is False, no message is printed
+            logging.raiseExceptions = False
+            sys.stderr = sio = io.StringIO()
+            root.warning('This is your final chance!')
+            self.assertEqual(sio.getvalue(), '')
+        finally:
+            sys.stderr = old_stderr
+            root.addHandler(self.root_hdlr)
+            logging.lastResort = old_lastresort
+            logging.raiseExceptions = old_raise_exceptions
+
+
 class BaseFileTest(BaseTest):
     "Base class for handler tests that write log files"
 
@@ -2017,6 +2051,7 @@
                  FormatterTest,
                  LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
                  RotatingFileHandlerTest,
+                 LastResortTest,
                  #TimedRotatingFileHandlerTest
                 )
 

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Fri Dec 10 12:42:57 2010
@@ -63,6 +63,8 @@
 Library
 -------
 
+- logging: added "handler of last resort". See http://bit.ly/last-resort-handler
+
 - test.support: Added TestHandler and Matcher classes for better support of
   assertions about logging.
 


More information about the Python-checkins mailing list