import collections import json class DebugLog(object): '''Debug logging class. The Python standard library contains the library called logging, which is fine for general purpose logging. For logs meant to help developers analyse problems, they are less than ideal. * Log files are essentially free-form text, with only minimal structure (timestamps), and that is unreliable to parse when log messages may contain newlines. * It is not very efficient. * Typically, a log grows quite large, making it use a lot of disk space. This is wasteful when most developers would only need the end of the log, at the point of a crash, to solve most problems. (But sometimes the whole log from the whole runtime would be good.) This class provides a different kind of logging. The log file is only written when requested, not continously, and is stored in a ring buffer, meaning only the last messages are written, with earlier ones forgotten. This is fine for crash logs. The logs are written in a format that is easily machine parseable (JSON). ''' DEFAULT_RING_BUFFER_SIZE = 1000 def __init__(self): self._max_buffer_size = self.DEFAULT_RING_BUFFER_SIZE self.clear() def set_ring_buffer_size(self, num_entries): self._max_buffer_size = num_entries while len(self._ring_buffer) > self._max_buffer_size: self._ring_buffer.popleft() def add_to_preamble(self, **kwargs): self._preamble.append(kwargs) def log(self, **kwargs): self._ring_buffer.append(kwargs) def write_ring_buffer(self, filename_or_handle): def write(f): f.write('[\n') first = True for q in [self._preamble, self._ring_buffer]: for entry in q: if first: first = False else: f.write(',\n') json.dump(entry, f, indent=4) f.write('\n]\n') if hasattr(filename_or_handle, 'write'): write(filename_or_handle) else: with open(filename_or_handle, 'a') as f: write(f) def clear(self): self._preamble = collections.deque([], self._max_buffer_size) self._ring_buffer = collections.deque([], self._max_buffer_size) if __name__ == '__main__': x = DebugLog() x.add_to_preamble(version='1.2') x.log(msg='yoyoyo', url='foobar') x.log(msg='blahblah', data=123) x.write_ring_buffer('/dev/stdout')