Last active
August 29, 2015 13:59
-
-
Save lemiant/10911072 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""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