-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathlog.py
98 lines (77 loc) · 3.09 KB
/
log.py
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
97
98
import inspect
import logging
class IndentFormatter(logging.Formatter):
"""
Format the given log messages with proper indentation based on the stack
depth of the code invoking the logger. This removes the need for manual
indentation using ``'\t'`` characters.
"""
@staticmethod
def identify_cut(filenames):
"""
Identify the depth at which the invoking function can be located. The
invoking function would be the first occurrence of a file just after
all stack filenames from within Python libs itself.
@param filenames: the names of all files from which logs were pushed
@return: the index of the filename from which the logger was called
"""
lib_string = 'lib/python'
lib_started = False
for index, filename in enumerate(filenames):
if not lib_started and lib_string in filename:
lib_started = True
if lib_started and lib_string not in filename:
return index
def __init__(self):
"""
Initialise the formatter with the fixed log format. The format is
intentionally minimal to get clean and readable logs.
"""
fmt = "%(asctime)s │ %(levelname)-8s │ %(indent)s%(function)s: %(message)s"
logging.Formatter.__init__(self, fmt=fmt)
self.baseline = None
self.cut = None
def format(self, record):
"""
Format the log message with additional data extracted from the stack.
@param record: the log record to format with this formatter
@return: the formatted log record
"""
stack = inspect.stack(context=0)
depth = len(stack)
if self.baseline is None:
self.baseline = depth
if self.cut is None:
filenames = map(lambda x: x.filename, stack)
self.cut = IndentFormatter.identify_cut(filenames)
# Inject custom information into the record
record.indent = ' ' * (depth - self.baseline)
record.function = stack[self.cut].function
# Format the record using custom information
out = logging.Formatter.format(self, record)
# Remove custom information from the record
del record.indent
del record.function
return out
def reset(self):
"""
Reset the baseline and cut attributes so that the next call to the
logger can repopulate them to the new values for the particular file.
"""
self.baseline = None
self.cut = None
def set_up_logging():
"""
Configure logging with some first-run configuration. This method must be
called only once from the main process.
"""
formatter = IndentFormatter()
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logging.basicConfig(level=logging.INFO, handlers=(handler,))
def reset_handler():
"""
Reset the formatter on the handler on the root logger. This causes the next
call to the logger can repopulate them based on the new stack in a new file.
"""
logging.root.handlers[-1].formatter.reset()