Skip to content

Instantly share code, notes, and snippets.

@lyuboraykov
Created August 26, 2014 14:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lyuboraykov/2ace496db9815154ae26 to your computer and use it in GitHub Desktop.
Save lyuboraykov/2ace496db9815154ae26 to your computer and use it in GitHub Desktop.
Log python calls in a tree-format
import sys
from datetime import datetime, date
class PrettyLogger(object):
"""
Class wrapper for a system trace function. That makes
human readable stack trace logs.
"""
def __init__(self, logfile=sys.stdout, max_indent=0):
"""
logfile -- the file where the logs will be appended.
max_indent -- the maximum depth of the logging tree. Zero for full tree.
Defaults to 0.
"""
self.logfile = logfile
self.indent = 0
self.time_stack = []
self.escaped_functions = ['_remove', '__call__']
self.max_indent = max_indent
def start_logging(self):
"""Registers the tracing function."""
sys.settrace(self._tracefunc)
def stop_logging(self):
"""Registers None as the trace function."""
sys.settrace(None)
def _tracefunc(self, frame, event, arg):
"""
Trace function that logs a tree representation of
the stack trace in a python script.
frame -- the current python frame object. See Frame Objects at
http://docs.python.org/2/reference/datamodel.html#types
event -- can be 'call', 'return', 'line', 'exception', 'c_call',
'c_return' or 'c_exception' depending on the function action.
See http://docs.python.org/2/library/sys.html#sys.settrace
arg -- depending on the event, None for the usages in this function.
"""
function_name = frame.f_code.co_name
if function_name in self.escaped_functions:
return
parameters = frame.f_locals.values()
parameters_string = ''.join(map(lambda p: '{}, '.format(p),
parameters))
parameters_string = parameters_string[:-2]
current_time = datetime.now().time().replace(microsecond=0)
if event == 'call':
self.indent += 1
if self.indent <= self.max_indent or self.max_indent == 0:
log_line = self.indent * ' |'
log_line = log_line[:-3] + '\\ \n'
self.logfile.write(log_line)
log_line = log_line[:-3] + ' ({}) [{}] {}({}) \n'.format(self.indent,
current_time,
function_name,
parameters_string)
self.time_stack.append(current_time)
self.logfile.write(log_line)
if event == 'return':
if self.indent <= self.max_indent or self.max_indent == 0:
started_time = self.time_stack.pop()
time_delta = datetime.combine(date.today(), current_time) \
- datetime.combine(date.today(), started_time)
log_line = ' |' * self.indent
self.logfile.write(log_line + '\n')
log_line = ' |' * self.indent
log_line = log_line[:-2] + '({}) [{}] \n'.format(self.indent, time_delta)
self.logfile.write(log_line)
self.indent -= 1
elif event == 'exception':
exception_string = '{0} Exception at {1}({2}) {0} \n'.format(10*'#',
function_name,
parameters_string)
self.logfile.write(exception_string)
self.logfile.write('Stopping logging... \n')
self.stop_logging()
return self._tracefunc
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
if __name__ == '__main__':
p = PrettyLogger()
p.start_logging()
fibonacci(5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment