summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVinay Sajip <vinay_sajip@yahoo.co.uk>2010-12-10 11:42:57 (GMT)
committerVinay Sajip <vinay_sajip@yahoo.co.uk>2010-12-10 11:42:57 (GMT)
commit5a27d4018692a9cb9f30b5ed81ac7061c8fe3c82 (patch)
tree13607ce1891690d5d533633bf673b3222a67a1dd
parentcf03ac0c64dba3ec33f9c3af1cbe7b387d1ca534 (diff)
downloadcpython-5a27d4018692a9cb9f30b5ed81ac7061c8fe3c82.zip
cpython-5a27d4018692a9cb9f30b5ed81ac7061c8fe3c82.tar.gz
cpython-5a27d4018692a9cb9f30b5ed81ac7061c8fe3c82.tar.bz2
logging: added handler of last resort.
-rw-r--r--Lib/logging/__init__.py64
-rw-r--r--Lib/test/test_logging.py35
-rw-r--r--Misc/NEWS2
3 files changed, 87 insertions, 14 deletions
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 04d9f30..e1c4a37 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -33,7 +33,7 @@ __all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR',
'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 @@ class FileHandler(StreamHandler):
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 @@ class Manager(object):
"""
self.root = rootnode
self.disable = 0
- self.emittedNoHandlerWarning = 0
+ self.emittedNoHandlerWarning = False
self.loggerDict = {}
self.loggerClass = None
self.logRecordFactory = None
@@ -1415,10 +1435,13 @@ class Logger(Filterer):
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 getLogger(name=None):
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 @@ fatal = critical
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 error(msg, *args, **kwargs):
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 @@ warn = warning
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 info(msg, *args, **kwargs):
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 debug(msg, *args, **kwargs):
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()
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index cd8cdbd..08fd7c4 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -1927,6 +1927,40 @@ class FormatterTest(unittest.TestCase):
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 @@ def test_main():
FormatterTest,
LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
RotatingFileHandlerTest,
+ LastResortTest,
#TimedRotatingFileHandlerTest
)
diff --git a/Misc/NEWS b/Misc/NEWS
index 82c8e0c..8f98949 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -63,6 +63,8 @@ Core and Builtins
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.