【问题标题】:Logging and exceptions in PythonPython 中的日志记录和异常
【发布时间】:2020-12-31 23:29:51
【问题描述】:

我目前正在编写我的第一个更大的脚本,它是一个基于控制台的 GUI,用户通过输入数字来选择选项来启动多个任务:

我最近实现了很多错误处理,以防止在后台出现问题时关闭窗口。我有点困惑我的方法是否正确。

我的代码的基本结构如下:

有一个函数 read_excel() 可以加载一些带有熊猫的 Excel 文件:

def read_excel(excel_path):
    
    try:
        df = pd.read_excel(excel_path, encoding="utf-8")
    except FileNotFoundError:
        raise FileNotFoundError('Unable to load assignment file, maybe choose custom')
        return

    # do stuff....

    if not all(len(x) == len(signalnames) for x in [frequencies, cans]):
        raise ValueError('Frequency and can number must be given for every signal in assignment file!')
    else:
        logging.info("Successfully loaded assignment file")

   return signalnames, frequencies, group_names, cans, can_paths

这个函数然后在GUI调用的函数ft_14()中与其他函数一起使用:

def ft_14(files, draft assignement_path):
    try:
        signalnames, frequencies, group_names, cans, can_paths = read_excel(assignement_path)
    except (ValueError, FileNotFoundError) as e:
        logging.error(e)
        return

        # do stuff..

        try:
            wb.save(os.path.join(os.path.dirname(files[0]), "FT.14_results.xlsx"))
            wb.close()
        except Exception:
            logging.error('Unable to save report excel')

所以我的尝试是在后端引发异常,然后在由 GUI 调用的函数中排除它们,并使用日志记录为用户显示它们。所以我的问题是这种方法是否是使用异常和一起记录的正确方法,或者是否有更聪明的方法,因为调用:

try:
    # some function()
except Exection as e:
    logging.error(e)

我觉得不对。

【问题讨论】:

  • 您的方法对我来说似乎很合理。如果函数失败,引发异常是一种非常 Python 的处理方式,而其他语言(例如 C 或 Go)会返回错误。只有一个建议,通过子类化Exception 创建您自己的异常类型,并在try: ... except ... 子句中使用它们而不是通用的Exception。我的建议是有一个包级别的异常,然后是从它继承的一堆异常:class MyPackageException(Exception): pass,然后是class CertainErrorTypeException(MyPackageException): pass
  • 感谢您的回答!我现在实现了我自己的异常类,它直接记录到当前的记录器。现在看起来干净多了。
  • 它对清晰度有很大帮助,但它还有另一个好处。当在通用 Exception 上执行 try ... except ... 子句时,您将捕获您不想处理的错误并处理它们,就好像它们是您正在寻找的错误一样,这会给您带来很多麻烦,直到您意识到什么被记录为,例如,价值出债券错误是TypeError,因为您使用str而不是int调用了一个函数。这种带有根错误的树状结构以及您希望从中继承的尽可能多的层和错误确实有助于调试。
  • 顺便说一句,但它不是 GUI,根据定义,它是基于控制台/文本的 UI,即 CLI。
  • @juanpa.arrivillaga 因为我们在吹毛求疵... :) 它不是 CLI,因为它是交互式的并且实际上并不使用命令行。我猜是TUI

标签: python exception logging


【解决方案1】:

您最后发布的代码让您感到不舒服,它是正确的,因为它按照它所说的去做。如果有异常,它会记录错误。

担心的是它也处理异常。在许多情况下,您不想更改异常处理过程中的任何其他内容——您只想记录并让异常正常传播。

https://docs.python.org/3/tutorial/errors.html#raising-exceptions

import logging
logger = logging.getLogger(__name__)

def fn(x):
    try:
        return x / 0
    except Exception as e:
        logger.error(str(e))
        raise e
    
print ("let's do something risky")
try:
    fn(20)
except Exception as e:
    pass
print ("it has been done")

注意 fn(x) 如何检测和处理异常,然后重新引发它们,就好像它根本没有对异常做任何事情一样?你可以用“raise”而不是参数来做到这一点。

在您的第一个代码示例中,您捕获了一个异常,然后引发了一个完全不同的异常,它恰好属于同一类型:

except FileNotFoundError:
    raise FileNotFoundError('Unable to load assignment file, maybe choose custom')

如果您想隐藏信息,这可能是公平的游戏,例如,如果异常包含您不想暴露给外部的内部细节。但是您不知道通过用新实例完全替换异常会丢失什么信息。但这些都不是“正确”或“错误”,它们只是您对如何处理异常做出的选择。 (编辑:最好最终处理它们或记录它可以抛出的内容,但我们离题了)

这些对记录器来说都不重要。你本可以在那里记录并重新加注。您可以记录原始异常,然后提升经过清理的版本。您可以在那里处理它,记录它,而不是提高(如您发布的示例)。记录器不关心你对异常做了什么。如果要记录,请立即记录。

【讨论】:

    猜你喜欢
    • 2011-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-08
    • 1970-01-01
    • 2014-04-27
    • 1970-01-01
    相关资源
    最近更新 更多