Reputation: 14090
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?
Upvotes: 5
Views: 1062
Reputation: 9881
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.
Upvotes: 4