Issue 1412054: More easily extensible logging module (original) (raw)
The Python logging.Logger class is supposed being extensible by overriding the makeRecord() method.
This is not completely true because the public logging method (log(), debug(), info()...) call the less public _log(), which in turn call makeRecord(). While the public methods have a signature such:
def info(self, msg, *args, **kwargs):
...
apply(self._log, (INFO, msg, args), kwargs)
thus leaving room for expansion, the _log() method is not so much flexible:
def _log(self, level, msg, args, exc_info=None):
# ...interesting stuff here...
record = self.makeRecord(self.name, level, fn,
lno, msg, args, exc_info) self.handle(record)
The makeRecord signature is:
def makeRecord(self, name, level, fn, lno, msg,
args, exc_info):
So if anybody wants to add a keyword to makeRecord, he also needs to re-implement the "interesting stuff" in the supposed private _log (finding the source row, reading the stack trace...).
The attached patch should leave the _log behavior unchanged but all unknown keyword arguments are passed to makeRecord.
If a wrong parameter is passed to any public method, the logger as before raises an exception, which is raised on the makeRecord() call instead of _log()'s.
With the patch on, use case for an user that want to log extra keywords is:
import logging
# Logger customization
class MyLogRecord(logging.LogRecord):
"""Add a foo to a log record."""
def __init__(self, name, level, pathname,
lineno, msg, args, exc_info, foo):
logging.LogRecord.__init__(self, name,
level, pathname, lineno, msg, args, exc_info) self.foo = foo
class MyLogger(logging.Logger):
"""Can log foos too."""
def makeRecord(self, name, level, fn,
lno, msg, args, exc_info, foo):
return MyLogRecord(name, level, fn,
lno, msg, args,
exc_info, foo)
class MyHandler(logging.Handler):
"""Can handle foo records."""
def emit(self, record):
if isinstance(record, MyLogRecord):
print self.format(record), record.foo
# Logger configuration
logging.setLoggerClass(MyLogger)
logger = logging.getLogger('test')
logger.setLevel(logging.INFO)
hndl = MyHandler(level=logging.INFO)
hndl.setFormatter(
logging.Formatter("%(message)s at line
#%(lineno)d")) logger.addHandler(hndl)
# Logger usage
logger.info("Message", foo='pippo')
# prints something like "Message at line #40 pippo"