How to prevent logging code from cluttering real code?

Refresh

December 2018

Views

238 time

4

I am working on some code where half the code seems to be logging and timing various things. I find this clutters up the real code. I was wondering what are the approaches to still logging everything but cleanly separating the logging and real code?

e.g. some block of code could do something as simple as:

for item in files:
    parsed_item = parse_item(item) 

but to log it could produce a lot more code which clutters up the real code e.g.

folders = 0
files = 0
paid_users = 0
free_users = 0

for item in files:
    parsed_item = parse_item(item)

    if parsed_item['isFolder']:
        folders += 1
    else:
        files += 1

    if parsed_item['isPaidUser']:
        paid_users += 1
    else:
        free_users += 1

logger.info('Parsed: %d folders %d files %d paid users %d free users' % folders, files, paid_users, free_users)

Potentially there could be something cleaner where we use a decorator or context manager for the block of code and then do some logging on the __exit__() method e.g.

with parse_files_logger() as logger:
    for item in files:
        parsed_item = parse_item(item)
        logger.log(parsed_item)

but keen to see if there are cleaner alternatives?

2 answers

1

Вы можете воспользоваться тем , что регистраторы могут быть переданы какой - либо другой объект , чем строка, и возвращает значение этого объекта из str(object)используется для определения сообщения. Следовательно:

class ParsedItemInfo(object):
    def __init__(self):
        self.folders = self.files = self.paid_users = self.free_users = 0

    def add(self, item):
        if item['isFolder']:
            self.folders += 1
        else:
            self.files += 1

        if item['isPaidUser']:
            self.paid_users += 1
        else:
            self.free_users += 1

    def __str__(self):
        return 'Parsed: %d folders %d files %d paid users %d free users' % (self.folders, self.files, self.paid_users, self.free_users)

а затем в ваш «реальный код»:

parsed_item_info = ParsedItemInfo()

for item in files:
    parsed_item = parse_item(item)
    parsed_item_info.add(parsed_item)

logger.info(parsed_item_info)
0

Определение некоторого регистратора класса (как в вашем примере контекста менеджера) кажется разумным для меня - то, как вы отдельный лог-посвященный анализу с самого кода.

Я также хотел бы разделить код на множество функций / методы делает именно один (по крайней мере, с точки зрения регистрации) вещей, а затем использовать декоратор (ы) для регистрации вызовов функций. В случае, если вы хотите отключить ведение журнала (например, в производственной версии коды) вы можете просто заменить ваши декоратор с Соской (функцией идентичности):

def decorator(f):
    def wrapper(*args):
        result = f(*args)
        logger.log(f.__name__, args, result)
        return result

    return wrapper

def decorator(f): # a dummy
    return f

...

@decorator
def functionToBeLogged(...):
    ...