Source code for loggingtools.log_with
import functools
import inspect
import logging
from itertools import chain
from timeit import default_timer as timer
import math
import sys
[docs]def format_time(timespan, precision=3):
"""Formats the timespan in a human readable form
Args:
timespan (float):
Time in seconds.
precision (int):
Desired precision.
"""
if timespan >= 60.0:
# we have more than a minute, format that in a human readable form
# Idea from http://snipplr.com/view/5713/
parts = [("d", 60 * 60 * 24), ("h", 60 * 60), ("min", 60), ("s", 1)]
time = []
leftover = timespan
for suffix, length in parts:
value = int(leftover / length)
if value > 0:
leftover %= length
time.append(u'%s%s' % (str(value), suffix))
if leftover < 1:
break
return " ".join(time)
# Unfortunately the unicode 'micro' symbol can cause problems in
# certain terminals.
# See bug: https://bugs.launchpad.net/ipython/+bug/348466
# Try to prevent crashes by being more secure than it needs to
# E.g. eclipse is able to print a ยต, but has no sys.stdout.encoding set.
units = [u"s", u"ms", u'us', "ns"] # the recordable value
if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding:
try:
u'\xb5'.encode(sys.stdout.encoding)
units = [u"s", u"ms", u'\xb5s', "ns"]
except:
pass
scaling = [1, 1e3, 1e6, 1e9]
if timespan > 0:
order = min(-int(math.floor(math.log10(timespan)) // 3), 3)
else:
order = 3
# return u"%.*g %s" % (precision, timespan * scaling[order], units[order])
return u"{:.1f} {}".format(timespan * scaling[order], units[order])
[docs]def format_args(args, kwargs, arg_names, ignore=set()):
"""Format args
Args:
args (tuple):
kwargs (dict):
arg_names (List[str]):
Returns:
str:
"""
def items():
for i, name in enumerate(arg_names):
if name in ignore:
continue
if i < len(args):
yield name, args[i]
elif name in kwargs:
yield name, kwargs[name]
d = ', '.join(('{}: {}'.format(*m) for m in items()))
return '{' + d + '}'
[docs]def format_func(function, qualname=False):
"""Format func
Args:
function (str):
Returns:
str:
"""
if qualname:
try:
return '<' + function.__qualname__ + '>'
except AttributeError:
pass
return '<' + function.__name__ + '>'
[docs]def log_with(logger=None, loglevel=logging.INFO, arguments=True, qualname=False,
timed=False, ignore=()):
"""Logging decorator
Args:
logger (Logger|None):
Instance of a logger
loglevel (int):
Level of logging
arguments(bool):
Flag whether to log arguments that are passed to the function
qualname (bool):
Flag whether to use __qualname__ instead of __name__ for getting
the name of the decorated function.
timed (bool):
Flag whether to log the execution time of the function.
ignore (Iterable[str]):
Argument names to ignore for logging arguments.
Examples:
>>> @log_with()
>>> def func():
>>> pass
"""
def decorator(func):
# If logger is not set, set module's logger.
_logger = logger if logger else logging.getLogger(func.__module__)
_ignore = set(ignore)
_arg_names = inspect.signature(func).parameters.keys()
_log = functools.partial(_logger.log, level=loglevel)
def message(*fmt: str) -> str:
return ' '.join(chain((format_func(func, qualname),), fmt))
@functools.wraps(func)
def wrapper(*args, **kwargs):
if arguments:
_log(msg=message(
format_args(args, kwargs, _arg_names, _ignore)))
start = timer()
result = func(*args, **kwargs)
end = timer()
if timed:
timespan = end - start
_log(msg=message('Time:', format_time(timespan)))
return result
return wrapper
return decorator