mirror of https://github.com/python/cpython
logging: added handler of last resort.
This commit is contained in:
parent
cf03ac0c64
commit
5a27d40186
|
@ -33,7 +33,7 @@ __all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR',
|
||||||
'captureWarnings', 'critical', 'debug', 'disable', 'error',
|
'captureWarnings', 'critical', 'debug', 'disable', 'error',
|
||||||
'exception', 'fatal', 'getLevelName', 'getLogger', 'getLoggerClass',
|
'exception', 'fatal', 'getLevelName', 'getLogger', 'getLoggerClass',
|
||||||
'info', 'log', 'makeLogRecord', 'setLoggerClass', 'warn', 'warning',
|
'info', 'log', 'makeLogRecord', 'setLoggerClass', 'warn', 'warning',
|
||||||
'getLogRecordFactory', 'setLogRecordFactory']
|
'getLogRecordFactory', 'setLogRecordFactory', 'lastResort']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import codecs
|
import codecs
|
||||||
|
@ -997,6 +997,26 @@ class FileHandler(StreamHandler):
|
||||||
self.stream = self._open()
|
self.stream = self._open()
|
||||||
StreamHandler.emit(self, record)
|
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
|
# Manager classes and functions
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
|
@ -1056,7 +1076,7 @@ class Manager(object):
|
||||||
"""
|
"""
|
||||||
self.root = rootnode
|
self.root = rootnode
|
||||||
self.disable = 0
|
self.disable = 0
|
||||||
self.emittedNoHandlerWarning = 0
|
self.emittedNoHandlerWarning = False
|
||||||
self.loggerDict = {}
|
self.loggerDict = {}
|
||||||
self.loggerClass = None
|
self.loggerClass = None
|
||||||
self.logRecordFactory = None
|
self.logRecordFactory = None
|
||||||
|
@ -1415,10 +1435,13 @@ class Logger(Filterer):
|
||||||
c = None #break out
|
c = None #break out
|
||||||
else:
|
else:
|
||||||
c = c.parent
|
c = c.parent
|
||||||
if (found == 0) and raiseExceptions and not self.manager.emittedNoHandlerWarning:
|
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"
|
sys.stderr.write("No handlers could be found for logger"
|
||||||
" \"%s\"\n" % self.name)
|
" \"%s\"\n" % self.name)
|
||||||
self.manager.emittedNoHandlerWarning = 1
|
self.manager.emittedNoHandlerWarning = True
|
||||||
|
|
||||||
def getEffectiveLevel(self):
|
def getEffectiveLevel(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1676,7 +1699,9 @@ def getLogger(name=None):
|
||||||
|
|
||||||
def critical(msg, *args, **kwargs):
|
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:
|
if len(root.handlers) == 0:
|
||||||
basicConfig()
|
basicConfig()
|
||||||
|
@ -1686,7 +1711,9 @@ fatal = critical
|
||||||
|
|
||||||
def error(msg, *args, **kwargs):
|
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:
|
if len(root.handlers) == 0:
|
||||||
basicConfig()
|
basicConfig()
|
||||||
|
@ -1694,15 +1721,18 @@ def error(msg, *args, **kwargs):
|
||||||
|
|
||||||
def exception(msg, *args, **kwargs):
|
def exception(msg, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Log a message with severity 'ERROR' on the root logger,
|
Log a message with severity 'ERROR' on the root logger, with exception
|
||||||
with exception information.
|
information. If the logger has no handlers, basicConfig() is called to add
|
||||||
|
a console handler with a pre-defined format.
|
||||||
"""
|
"""
|
||||||
kwargs['exc_info'] = True
|
kwargs['exc_info'] = True
|
||||||
error(msg, *args, **kwargs)
|
error(msg, *args, **kwargs)
|
||||||
|
|
||||||
def warning(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:
|
if len(root.handlers) == 0:
|
||||||
basicConfig()
|
basicConfig()
|
||||||
|
@ -1712,7 +1742,9 @@ warn = warning
|
||||||
|
|
||||||
def info(msg, *args, **kwargs):
|
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:
|
if len(root.handlers) == 0:
|
||||||
basicConfig()
|
basicConfig()
|
||||||
|
@ -1720,7 +1752,9 @@ def info(msg, *args, **kwargs):
|
||||||
|
|
||||||
def debug(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:
|
if len(root.handlers) == 0:
|
||||||
basicConfig()
|
basicConfig()
|
||||||
|
@ -1728,7 +1762,9 @@ def debug(msg, *args, **kwargs):
|
||||||
|
|
||||||
def log(level, 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:
|
if len(root.handlers) == 0:
|
||||||
basicConfig()
|
basicConfig()
|
||||||
|
|
|
@ -1927,6 +1927,40 @@ class FormatterTest(unittest.TestCase):
|
||||||
f = logging.Formatter('asctime', style='$')
|
f = logging.Formatter('asctime', style='$')
|
||||||
self.assertFalse(f.usesTime())
|
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):
|
class BaseFileTest(BaseTest):
|
||||||
"Base class for handler tests that write log files"
|
"Base class for handler tests that write log files"
|
||||||
|
|
||||||
|
@ -2017,6 +2051,7 @@ def test_main():
|
||||||
FormatterTest,
|
FormatterTest,
|
||||||
LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
|
LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
|
||||||
RotatingFileHandlerTest,
|
RotatingFileHandlerTest,
|
||||||
|
LastResortTest,
|
||||||
#TimedRotatingFileHandlerTest
|
#TimedRotatingFileHandlerTest
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,8 @@ Core and Builtins
|
||||||
Library
|
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
|
- test.support: Added TestHandler and Matcher classes for better support of
|
||||||
assertions about logging.
|
assertions about logging.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue