【问题标题】:How to prevent logging code from cluttering real code?如何防止日志代码混淆真实代码?
【发布时间】:2016-06-16 10:47:47
【问题描述】:

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

例如一些代码块可以做一些简单的事情:

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)

但很想看看是否有更清洁的替代品?

【问题讨论】:

    标签: python logging coding-style


    【解决方案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)
    

    【讨论】:

      【解决方案2】:

      某些记录器类的定义(如在您的上下文管理器示例中)对我来说似乎是明智的 - 这样您就可以将日志专用分析与代码本身分开。

      我还将代码拆分为许多函数/方法,只做一件事情(至少从日志记录的角度来看),然后使用装饰器来记录函数的调用。如果您想禁用日志记录(例如在代码的生产版本中),您可以用假人(身份功能)替换您的装饰器:

      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(...):
          ...
      

      【讨论】:

        【解决方案3】:

        我使用两种方法将这样的代码移开。但它们并没有真正使代码更干净。正如我所说,他们只是将代码排除在外。

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

        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)
        

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

        ...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")
        

        我不是其中任何一个的忠实拥护者,我也不是日志语句使我的代码更难阅读的忠实拥护者。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-04-08
          • 2014-05-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-04-04
          • 2011-05-14
          相关资源
          最近更新 更多