This helper function simplifies the difficult task of setting up and maintaining a logging system. Changes to a logging system can cause unanticipated consequences such as lost messages or duplicates. Debugging a logging hierarchy can be a tedious task. This function overrides the internal __repr__ functions of the internal classes and allows a print statement to generate the complete logger hierarchy with its associated internals. It allows easy debugging a logger and allows changes to be easily detected.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | import logging
import logging.handlers
def enable_logger_print():
def _logger_str(self):
s = ''
if self.parent != None:
s = _logger_str(self.parent)
s += """\n%s\n""" % (self.name)
for name,value in self.__dict__.items():
if name == 'parent': value = value and value.name or 'None'
if name == 'level': value = '%s (%s)' %(value,logging.getLevelName(value))
s += ' %s = %s\n' % (name,value)
return s
def _handler_repr(handlername):
def __repr__(self):
s = '\n\t%s\n' % (handlername)
for name,value in self.__dict__.items():
if name == 'level': value = '%s (%s)' %(value,logging.getLevelName(value))
s += '\t %s = %s\n' % (name,value)
s += '\t'
return s
return __repr__
def _formatter_repr(self):
s = ""
for name,value in self.__dict__.items():
s += '\n\t\t%s = %s' % (name,value)
return s
def _manager_repr(self):
s = ""
for name,value in self.__dict__.items():
if name == 'root': value = value.name
if name == 'loggerDict': value = _logger_list(); name='loggerDict keys'
s += '\n %s = %s' % (name,value)
return s
def _filter_repr(self):
return self.name
def _logger_list():
return sorted([name for name in logging.Logger.manager.loggerDict])
for name in dir():
if name.endswith('Handler'):
logging.__dict__[name].__repr__ = _handler_repr(name)
for name in dir(logging):
if name.endswith('Handler'):
logging.__dict__[name].__repr__ = _handler_repr(name)
for name in dir(logging.handlers):
if name.endswith('Handler'):
logging.handlers.__dict__[name].__repr__ = _handler_repr(name)
logging.Logger.__str__ = _logger_str
logging.Formatter.__repr__ = _formatter_repr
logging.Filter.__repr__ = _filter_repr
logging.Manager.__repr__ = _manager_repr
if __name__ == '__main__':
logging.basicConfig( level=logging.INFO)
logger = logging.getLogger('')
logger1 = logging.getLogger('a.b.c')
logger2 = logging.getLogger('x.a')
rootLogger = logging.getLogger()
rootLogger.setLevel(logging.DEBUG)
streamHandler = logging.StreamHandler()
streamHandler.name = 'Stream Server'
frmt = logging.Formatter("%(filename)s - %(name)s - %(levelname)s - %(message)s")
streamHandler.setFormatter(frmt)
fltr = logging.Filter('x')
streamHandler.addFilter(fltr)
logger.addHandler(streamHandler)
print '='*80
print '\n*** print logger2 BEFORE enable_logger_print() ***\n'
print logger2
print '='*80
enable_logger_print() # allows printing logger internals
print '\n*** print logger2 AFTER enable_logger_print() ***\n'
print logger2
print '='*80
logger.info('Help Me (root)')
logger1.info('Help Me (a.b.c)')
logger2.info('Help Me (x)')
|
Executing the program generates the following output:
================================================================================
*** print logger2 BEFORE enable_logger_print() ***
<logging.Logger object at 0x027983F0>
================================================================================
*** print logger2 AFTER enable_logger_print() ***
root
name = root
parent = None
handlers = [
StreamHandler
stream = <idlelib.rpc.RPCProxy object at 0x02754790>
level = 0 (NOTSET)
lock = <_RLock owner=None count=0>
_name = None
filters = []
formatter =
datefmt = None
_fmt = %(levelname)s:%(name)s:%(message)s
,
StreamHandler
stream = <idlelib.rpc.RPCProxy object at 0x02754790>
level = 0 (NOTSET)
lock = <_RLock owner=None count=0>
_name = Stream Server
filters = [x]
formatter =
datefmt = None
_fmt = %(filename)s - %(name)s - %(levelname)s - %(message)s
]
level = 10 (DEBUG)
disabled = 0
propagate = 1
filters = []
x.a
name = x.a
parent = root
handlers = []
level = 0 (NOTSET)
disabled = 0
manager =
emittedNoHandlerWarning = 0
disable = 0
root = root
loggerDict keys = ['a', 'a.b', 'a.b.c', 'x', 'x.a']
loggerClass = None
propagate = 1
filters = []
================================================================================
INFO:root:Help Me (root)
INFO:a.b.c:Help Me (a.b.c)
INFO:x.a:Help Me (x)
log_test.py - x.a - INFO - Help Me (x)
Both StreamHandlers generated Help Me (x) which, for this example, is a mistake. The call to basicConfig created a default StreamHandler and a default formatter. The second StreamHandler was added to the root logger with the defined formatter and a filter for x messages. All x messages are sent to both stream handlers which duplicates the message. The StreamHandler should have been added to logger2 and not the root logger. The logger2 propagate should also have been set false to keep x messages from propogating to the root logger. This would display only one x message per record, as expected.
replace:
logger.addHandler(streamHandler)
with:
logger2.addHandler(streamHandler)
logger2.propagate = 0
to fix the problem