"""A simple logging framework. I've brought this simple set of logging functions far and wide and they've always done me well. It is fast, it works, it's consistent. This module is imported by anything that needs logging. The options are set by the Main program. That's it: since modules are singletons, all modules know the verbosity and other settings for the logger. """ import sys, time, datetime def default(): dt = datetime.datetime.now() return "%02d:%02d:%02d.%02d" % (dt.hour, dt.minute, dt.second, dt.microsecond/10000) def win32safe(f): def inner(*args, **kwargs): text = args[0] if sys.platform.startswith('win'): try: import uromkan text = uromkan.dekana(text) except ImportError: pass text = text.encode('mcbs') return f(*args) return inner """Options for the logging framework. rjust_width: the justification for the ' > ' portion of the logged messages (default: 7) verbose: the verbosity setting (default: 0) timestamps: add timestamps (default: True) time_format: either a string for time.strftime() or a callable that returns a string with a timestamp (default: 'log.default') debug: debuging (default: True for test builds, False for others) exit: a callable that will exit the program properly (default: sys.exit(1) """ options = { 'rjust_width' : 8, 'verbose' : 0, 'timestamps' : True, 'time_format' : default, 'debug' : False, 'exit' : lambda: sys.exit(1) } # this function has been extensively profiled.. i couldn't find a faster # implementation that remained correct. removing the 'option' of having # a timestamp or not does not significantly decrease running time (.5 sec # over 1 million runs on a dual core 2.2 gHz pentium processor def timestamp(): """Get the timestamp; return '' if options['timestamps'] is false.""" if not options['timestamps']: return '' elif callable(options['time_format']): return options['time_format']() + ' ' if isinstance(options['time_format'], str): return time.strftime(options['time_format']) + ' ' @win32safe def error(msg='(appologies, no error text for this error)'): """Prints out a message msg, then exits the program.""" print >>sys.stderr, timestamp() + 'ERR > '.rjust(options['rjust_width']) + str(msg) options['exit']() @win32safe def errorne(msg): """Prints out an error msg, but does not exit.""" print >>sys.stderr, timestamp() + 'ERR > '.rjust(options['rjust_width']) + str(msg) @win32safe def warn(msg): """Prints out a warning.""" print timestamp() + 'WARN > '.rjust(options['rjust_width']) + str(msg) def v(msg): vn(msg, 1) def vv(msg): vn(msg, 2) def d(msg, prefix='DEBUG > '): if options['debug']: p(msg, prefix) @win32safe def vn(msg, level): if options['verbose'] >= level: prefix = '' if options['debug']: prefix = 'v' * level + ' > ' prefix = prefix.rjust(options['rjust_width']) prefix = timestamp() + prefix print prefix + str(msg) @win32safe def p(msg, prefix=' > '): ts = timestamp() lines = str(msg).split("\n") for line in lines: print ts + prefix.rjust(options['rjust_width']) + line _once_cache=[] def once(msg, type=p): if msg in _one_cache: return type(msg) _once_cache.append(msg) def timer(func): """A decorator that prints a function and it's arguments before and after it's execution, meant to allow for quick suspicion based profiling.""" def f(*args, **kwargs): p('%s(%s, %s) starting' % (func.__name__, args, kwargs), prefix='++t > ') val = func(*args, **kwargs) p('%s(%s, %s) ending' % (func.__name__, args, kwargs), prefix='--t > ') return val f.__name__ = func.__name__ f.__doc__ = func.__doc__ return f try: import psyco psyco.bind(timestamp) psyco.bind(default) # psyco doesn't work on the print functions themselves because # they have free variables thanks to the win32safe deco except Exception, ex: d("psyco support is disabled") import traceback traceback.print_exc()