Skip to content

Instantly share code, notes, and snippets.

@lemiant
Last active August 29, 2015 13:59
Show Gist options
  • Save lemiant/10911072 to your computer and use it in GitHub Desktop.
Save lemiant/10911072 to your computer and use it in GitHub Desktop.
"""Ultra-minimalist logger with context
e.g.:
from context_log import Log
Log.out = open('report.txt')
Log.formatter = lambda dictionary: "%(filename)s>%(lineno)03d: %(session)s: %message" % dictionary
Log.context['session'] = 'Cammping Trip'
Log.warn("There's a bear behind you")
# example.py>7: Camping Trip: There's a bear behind you
Log.debug("jk")
# example.py>10: Camping Trip: jk
"""
import threading, sys, json, os
class Logger(object):
def __init__(self):
self.event_types = {
'INFO': 0,
'DEBUG': 1,
'WARNING': 5,
'WARN': 5,
'ERROR': 10
}
self.universal = {}
self._context_dicts = {}
self.config() #Initialize
def config(self, event_types={}, minimum=5, out=sys.stdout, formatter=json.dumps):
"""Configure a batch of parameters. You can also change the attributes manually."""
self.event_types.update(event_types)
self.minimum = event_types[minimum] if isinstance(minimum, basestring) else minimum
self.formatter = formatter
self.out = out
def __getattr__(self, name):
if name == "context":
return self._context_dicts.setdefault(threading.current_thread(), {})
elif name.upper() in self.event_types:
def output(event="", **kwargs):
if self.event_types[name.upper()] >= self.minimum:
_dict = dict(
self.universal.items() +
self.context.items() +
kwargs.items() +
zip(('filename', 'lineno', 'func_name'), findCaller()) +
{'event': event, 'type': name.upper()}.items()
)
self.out.write(str(self.formatter(_dict))+'\n')
return output
else:
raise AttributeError(name+" is not in event_types")
# Pulled from logging.py in the stdlib
def findCaller():
"""Find the stack frame of the caller so that we can note the source file name, line number and function name."""
f = sys._getframe().f_back
rv = "(unknown file)", 0, "(unknown function)"
while hasattr(f, "f_code"):
co = f.f_code
filename = co.co_filename.rsplit('.',1)[0].lower()
_this_file = __file__.rsplit('.',1)[0].lower()
if filename == _this_file:
f = f.f_back
else:
rv = (os.path.basename(co.co_filename), f.f_lineno, co.co_name)
break
return rv
Log = Logger() #IMPORT THIS! Using one instance for all modules is what gives us persistent context
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment