logging: Added getLogRecordFactory/setLogRecordFactory with docs and tests.

This commit is contained in:
Vinay Sajip 2010-12-03 11:50:38 +00:00
parent 97cbb76ee3
commit 615615291f
4 changed files with 88 additions and 19 deletions

View File

@ -750,6 +750,19 @@ functions.
# ... override behaviour here # ... override behaviour here
.. function:: getLogRecordFactory()
Return a callable which is used to create a :class:`LogRecord`.
.. versionadded:: 3.2
This function has been provided, along with :func:`setLogRecordFactory`,
to allow developers more control over how the :class:`LogRecord`
representing a logging event is constructed.
See :func:`setLogRecordFactory` for more information about the how the
factory is called.
.. function:: debug(msg, *args, **kwargs) .. function:: debug(msg, *args, **kwargs)
Logs a message with level :const:`DEBUG` on the root logger. The *msg* is the Logs a message with level :const:`DEBUG` on the root logger. The *msg* is the
@ -973,6 +986,34 @@ functions.
function is typically called before any loggers are instantiated by applications function is typically called before any loggers are instantiated by applications
which need to use custom logger behavior. which need to use custom logger behavior.
.. function:: setLogRecordFactory(factory)
Set a callable which is used to create a :class:`LogRecord`.
:param factory: The factory callable to be used to instantiate a log record.
.. versionadded:: 3.2
This function has been provided, along with :func:`getLogRecordFactory`, to
allow developers more control over how the :class:`LogRecord` representing
a logging event is constructed.
The factory has the following signature.
factory(name, level, fn, lno, msg, args, exc_info, func=None, sinfo=None, \*\*kwargs)
:name: The logger name.
:level: The logging level (numeric).
:fn: The full pathname of the file where the logging call was made.
:lno: The line number in the file where the logging call was made.
:msg: The logging message.
:args: The arguments for the logging message.
:exc_info: An exception tuple, or None.
:func: The name of the function or method which invoked the logging
call.
:sinfo: A stack traceback such as is provided by
:func:`traceback.print_stack`, showing the call hierarchy.
:kwargs: Additional keyword arguments.
.. seealso:: .. seealso::
@ -3244,6 +3285,29 @@ wire).
messages, whose ``__str__`` method can return the actual format string to messages, whose ``__str__`` method can return the actual format string to
be used. be used.
.. versionchanged:: 3.2
The creation of a ``LogRecord`` has been made more configurable by
providing a factory which is used to create the record. The factory can be
set using :func:`getLogRecordFactory` and :func:`setLogRecordFactory`
(see this for the factory's signature).
This functionality can be used to inject your own values into a
LogRecord at creation time. You can use the following pattern::
old_factory = logging.getLogRecordFactory()
def record_factory(*args, **kwargs):
record = old_factory(*args, **kwargs)
record.custom_attribute = 0xdecafbad
return record
logging.setLogRecordFactory(record_factory)
With this pattern, multiple factories could be chained, and as long
as they don't overwrite each other's attributes or unintentionally
overwrite the standard attributes listed above, there should be no
surprises.
.. _logger-adapter: .. _logger-adapter:
LoggerAdapter Objects LoggerAdapter Objects

View File

@ -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',
'getLogRecordClass', 'setLogRecordClass'] 'getLogRecordFactory', 'setLogRecordFactory']
try: try:
import codecs import codecs
@ -238,7 +238,7 @@ class LogRecord(object):
information to be logged. information to be logged.
""" """
def __init__(self, name, level, pathname, lineno, def __init__(self, name, level, pathname, lineno,
msg, args, exc_info, func=None, sinfo=None): msg, args, exc_info, func=None, sinfo=None, **kwargs):
""" """
Initialize a logging record with interesting information. Initialize a logging record with interesting information.
""" """
@ -322,21 +322,24 @@ class LogRecord(object):
# #
# Determine which class to use when instantiating log records. # Determine which class to use when instantiating log records.
# #
_logRecordClass = LogRecord _logRecordFactory = LogRecord
def setLogRecordClass(cls): def setLogRecordFactory(factory):
""" """
Set the class to be used when instantiating a log record. Set the class to be used when instantiating a log record.
"""
global _logRecordClass
_logRecordClass = cls
def getLogRecordClass(): :param factory: A callable which will be called to instantiate
a log record.
"""
global _logRecordFactory
_logRecordFactory = factory
def getLogRecordFactory():
""" """
Return the class to be used when instantiating a log record. Return the class to be used when instantiating a log record.
""" """
return _logRecordClass return _logRecordFactory
def makeLogRecord(dict): def makeLogRecord(dict):
""" """
@ -345,7 +348,7 @@ def makeLogRecord(dict):
a socket connection (which is sent as a dictionary) into a LogRecord a socket connection (which is sent as a dictionary) into a LogRecord
instance. instance.
""" """
rv = _logRecordClass(None, None, "", 0, "", (), None, None) rv = _logRecordFactory(None, None, "", 0, "", (), None, None)
rv.__dict__.update(dict) rv.__dict__.update(dict)
return rv return rv
@ -1056,7 +1059,7 @@ class Manager(object):
self.emittedNoHandlerWarning = 0 self.emittedNoHandlerWarning = 0
self.loggerDict = {} self.loggerDict = {}
self.loggerClass = None self.loggerClass = None
self.logRecordClass = None self.logRecordFactory = None
def getLogger(self, name): def getLogger(self, name):
""" """
@ -1100,12 +1103,12 @@ class Manager(object):
+ klass.__name__) + klass.__name__)
self.loggerClass = klass self.loggerClass = klass
def setLogRecordClass(self, cls): def setLogRecordFactory(self, factory):
""" """
Set the class to be used when instantiating a log record with this Set the class to be used when instantiating a log record with this
Manager. Manager.
""" """
self.logRecordClass = cls self.logRecordFactory = factory
def _fixupParents(self, alogger): def _fixupParents(self, alogger):
""" """
@ -1305,7 +1308,7 @@ class Logger(Filterer):
A factory method which can be overridden in subclasses to create A factory method which can be overridden in subclasses to create
specialized LogRecords. specialized LogRecords.
""" """
rv = _logRecordClass(name, level, fn, lno, msg, args, exc_info, func, rv = _logRecordFactory(name, level, fn, lno, msg, args, exc_info, func,
sinfo) sinfo)
if extra is not None: if extra is not None:
for key in extra: for key in extra:

View File

@ -1799,7 +1799,7 @@ class ChildLoggerTest(BaseTest):
class DerivedLogRecord(logging.LogRecord): class DerivedLogRecord(logging.LogRecord):
pass pass
class LogRecordClassTest(BaseTest): class LogRecordFactoryTest(BaseTest):
def setUp(self): def setUp(self):
class CheckingFilter(logging.Filter): class CheckingFilter(logging.Filter):
@ -1817,17 +1817,17 @@ class LogRecordClassTest(BaseTest):
BaseTest.setUp(self) BaseTest.setUp(self)
self.filter = CheckingFilter(DerivedLogRecord) self.filter = CheckingFilter(DerivedLogRecord)
self.root_logger.addFilter(self.filter) self.root_logger.addFilter(self.filter)
self.orig_cls = logging.getLogRecordClass() self.orig_factory = logging.getLogRecordFactory()
def tearDown(self): def tearDown(self):
self.root_logger.removeFilter(self.filter) self.root_logger.removeFilter(self.filter)
BaseTest.tearDown(self) BaseTest.tearDown(self)
logging.setLogRecordClass(self.orig_cls) logging.setLogRecordFactory(self.orig_factory)
def test_logrecord_class(self): def test_logrecord_class(self):
self.assertRaises(TypeError, self.root_logger.warning, self.assertRaises(TypeError, self.root_logger.warning,
self.next_message()) self.next_message())
logging.setLogRecordClass(DerivedLogRecord) logging.setLogRecordFactory(DerivedLogRecord)
self.root_logger.error(self.next_message()) self.root_logger.error(self.next_message())
self.assert_log_lines([ self.assert_log_lines([
('root', 'ERROR', '2'), ('root', 'ERROR', '2'),
@ -2015,7 +2015,7 @@ def test_main():
ConfigFileTest, SocketHandlerTest, MemoryTest, ConfigFileTest, SocketHandlerTest, MemoryTest,
EncodingTest, WarningsTest, ConfigDictTest, ManagerTest, EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
FormatterTest, FormatterTest,
LogRecordClassTest, ChildLoggerTest, QueueHandlerTest, LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
RotatingFileHandlerTest, RotatingFileHandlerTest,
#TimedRotatingFileHandlerTest #TimedRotatingFileHandlerTest
) )

View File

@ -33,6 +33,8 @@ Core and Builtins
Library Library
------- -------
- logging: Added getLogRecordFactory/setLogRecordFactory with docs and tests.
- Issue #10549: Fix pydoc traceback when text-documenting certain classes. - Issue #10549: Fix pydoc traceback when text-documenting certain classes.
- Issue #2001: New HTML server with enhanced Web page features. Patch by Ron - Issue #2001: New HTML server with enhanced Web page features. Patch by Ron