Skip to content

Instantly share code, notes, and snippets.

@a-recknagel
Last active June 22, 2020 09:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save a-recknagel/03221b29514dc38d84712510d4f87d38 to your computer and use it in GitHub Desktop.
Save a-recknagel/03221b29514dc38d84712510d4f87d38 to your computer and use it in GitHub Desktop.
log arg decorator
>>> from logging.config import dictConfig
>>> dictConfig(
... {
... "version": 1,
... "disable_existing_loggers": False,
... "formatters": {
... "standard": {
... "format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
... }
... },
... "handlers": {
... "default": {
... "level": 10,
... "formatter": "standard",
... "class": "logging.StreamHandler",
... "stream": "ext://sys.stdout",
... }
... },
... "loggers": {"": {"handlers": ["default"], "level": 10}}
... }
... )
>>> @log_args()
... def foo(a, b=1, *args, **kwargs):
... return "FOO"
...
>>> foo(0)
2020-06-22 11:01:19,157 [INFO] __main__: foo(a=0)
'FOO'
from logging import getLogger, _nameToLevel
from inspect import signature
from functools import wraps
def log_args(loglevel_name="INFO", max_chars=20):
"""This decorator logs the arguments passed to a function before calling it.
Default loglevel is INFO and default argument truncation threshold is 20 character. If
you want to disable truncation, pass -1 instead.
"""
if loglevel_name not in _nameToLevel:
raise ValueError(f"'{loglevel_name}' is not a valid log level name. Please pick one of {[*_nameToLevel]} instead.")
loglevel = _nameToLevel[loglevel_name]
def outer(func):
logger = getLogger(func.__module__)
parameters = signature(func).parameters
@wraps(func)
def inner(*args, **kwargs):
# map arg- and kwarg-strings to their parameter names
parameter_map = (
[[param[0], str(arg)] for arg, param in zip(args, parameters)] +
[[name, str(value)] for name, value in kwargs.items()]
)
# truncate values, if necessary. this can probably be handled somewhat nicer.
for mapping in parameter_map:
if max_chars < 0:
continue
if len(mapping[1]) > max_chars:
mapping[1] = f"{mapping[1][:max_chars]} ..."
# build a string representing the call ...
parameter_string = ", ".join(f"{name}={value}" for name, value in parameter_map)
# ... and log it
logger.log(loglevel, f"{func.__name__}({parameter_string})")
return func(*args, **kwargs)
return inner
return outer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment