I would like to have python logging output to be in form of tree corresponding to logger tree. Just look at example.
Lets say we have a code:
import logging
logger_a = logging.getLogger("a")
logger_a_b = logging.getLogger("a.b")
logger_a_b_c = logging.getLogger("a.b.c")
# ...
logger_a.debug("One")
logger_a_b.warning("two")
logger_a_b.warning("three")
logger_a_b_c.critical("Four")
logger_a_b.warning("Five")
The output should be something like:
<--"a"
| DEBUG: One
|
o<--"a.b"
| | WARNING: Two
| | WARNING: Three
| |
| o<--"a.b.c"
| | CRITICAL: Four
| |
| | WARNING: Five
I could write formatters for each of the log by hand, but it doesn't solve the problem of inserting something like o<--"a.b" right and I would prefer to calculate offset automatically by logging structure.
There is a module called logging tree. It prints the logging layout. What I would like, is to print log messages approximately the same way.
Do you know any libraries, ways of doing it easy way?
Based on your example, I created a custom Formatter
which will handle the tree.
import logging
# custom tree formatter
class TreeFormatter(logging.Formatter):
formatPrefix = {} # map loggername, formatPrefix
def format(self, record):
s = ""
# first time this name is encountered: create the prefix and print the name
if not record.name in self.formatPrefix:
f = self.getFormatPrefix(record)
s += "%s \"%s\"\n" % (f, record.name)
# print the actual message
s += "%s %s: %s" % (self.formatPrefix[record.name], record.levelname, record.msg)
return s
# create the format prefix for the given package name
# (stored in self.formatPrefix[record.name])
# and return the first line to print
def getFormatPrefix(self, record):
depth = record.name.count(".")
self.formatPrefix[record.name] = " |" * (depth+1)
if depth == 0:
return "<--"
return "%so<--" % ( (" |" * depth)[:-1])
You can then use it to create the first-level logger (here a). The rest of the code is unchanged.
Here is an example:
# use this to create the first level logger
def createTreeLogger(name, level=logging.DEBUG):
logger = logging.getLogger(name)
logger.setLevel(level)
ch = logging.StreamHandler()
ch.setLevel(level)
ch.setFormatter(TreeFormatter())
logger.addHandler(ch)
return logger
if __name__ == '__main__':
logger_a = createTreeLogger("a") # first level: use createLogger
# then create your loggers as always
logger_a_b = logging.getLogger("a.b")
logger_a_b_c = logging.getLogger("a.b.c")
logger_a.debug("One")
logger_a_b.warning("two")
logger_a_b.warning("three")
logger_a_b_c.critical("Four")
logger_a_b.warning("Five")
logger_a.warning("Six")
What is nice is that the logging package internals will automatically use the same handler for the subpackages (a.b, a.b.c). So, by running this code, you get:
<-- "a"
| DEBUG: One
o<-- "a.b"
| | WARNING: two
| | WARNING: three
| o<-- "a.b.c"
| | | CRITICAL: Four
| | WARNING: Five
| WARNING: Six
One drawback is that the logs become confusing if you have more than one package hierarchy. But the TreeFormatter
class is easy to tweak to your needs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With