如何防止日志代码混乱实际代码?

时间:2016-06-16 10:47:48

标签: python logging coding-style

我正在研究一些代码,其中一半的代码似乎是记录和计时各种事情。我发现真正的代码混乱了。我想知道仍然记录所有内容但是干净地将日志记录和实际代码分开的方法是什么?

e.g。一些代码块可以做一些简单的事情:

for item in files:
    parsed_item = parse_item(item) 

但要记录它可能会产生更多代码,这会使实际代码混乱,例如。

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)

在我们对代码块使用装饰器或上下文管理器然后对__exit__()方法进行一些记录时,可能会有更清洁的东西,例如。

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

但是想知道是否有更清洁的替代方案?

3 个答案:

答案 0 :(得分: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)

答案 1 :(得分: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(...):
    ...

答案 2 :(得分:0)

我使用两种方法将这样的代码移开。但它们并没有真正使代码更清晰。正如我所说,他们只是将代码推开。

方法一: 在要移开的行周围使用括号

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)

它可能不会在堆栈溢出时显示得那么好,但是通过将日志语句括在括号中,您可以将任何内容移动到左括号右侧的任何位置。左括号必须符合缩进顺序。右括号可以放在任何你想要的地方。
另一个例子:

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

因此您可以根据需要将日志语句向左或向右推送。

方法二: 使用分号

假设你有一些这样的代码:

login()
logger.info('User has logged in')

如果在 login() 后添加分号,您可以将该日志语句移至与 login() 相同的行。
例如

login();                                          logger.info('User has logged in')

您可以根据需要将日志语句推送到最右边。 我喜欢这个,但是如果将来 login() 语句的长度发生变化,它将移动同一行上的日志。
因此,在将来更新代码时,您尝试将所有日志放在右侧均匀距离的情况可能会发生变化。
所以当你有这个时:

validate_password();                 logger.info("checked password")
validate_username();                 logger.info("checked username")
login();                             logger.info("logged in")

可能会变成这样:

validate_password(password);                 logger.info("checked password")
validate_username(un);                 logger.info("checked username")
login(username, password);                             logger.info("logged in")

我不是这两者的忠实拥护者,也不是使我的代码更难阅读的日志语句的忠实拥护者。