如何正确处理 logging.config.fileConfig 抛出的 IOError?

3

这可能是一个开放性的或者棘手的问题,但是我发现在处理异常时越来越多的情况下我不知道最佳的处理方式。

如果你试图使用不存在的文件配置FileHandler,Python 的logging模块会引发IOError。该模块不处理此异常,而是直接引发它。通常情况下,这是由于文件路径不存在(因此文件不存在),所以我们必须创建沿着路径的目录,如果想要处理异常并继续运行。

我希望我的应用程序能够正确地处理这个错误,因为每个用户都问过我们为什么不为他们创建合适的目录。

我决定处理这个问题的方式如下所示。

done = False
while not done:
    try:
        # Configure logging based on a config file
        # if a filehandler's full path to file does not exist, it raises an IOError
        logging.config.fileConfig(filename)

    except IOError as e:
        if e.args[0] == 2 and e.filename:
            # If we catch the IOError, we can see if it is a "does not exist" error
            # and try to recover by making the directories

            print "Most likely the full path to the file does not exist, so we can try and make it"
            fp = e.filename[:e.rfind("/")]

            # See https://dev59.com/L3VC5IYBdhLWcg3wfxU8 for why I don't just leap
            if not os.path.exists(fp):
                os.makedirs(fp)

        else:
            print "Most likely some other error...let's just reraise for now"
            raise
    else:
        done = True

我需要循环(或递归)因为有N个FileHandlers需要配置,因此会发生N个IOErrors并需要对其进行纠正。

这样做是否正确?是否存在更好的、更符合Python规范的方式,我不知道或者可能没有理解到?


我不确定不存在的文件是配置文件(传递给fileConfig,就像你的源代码中一样),还是日志文件的名称(根据你的问题文本 - “尝试使用不存在的文件配置FileHandler”)。我的回答是针对后者的。通常,在指定文件(无论是配置文件还是日志文件)时,有很多需要确保的事情 - 例如,一个文件可能已经存在,但由于权限问题而无法读取或写入。没有一种“一刀切”的解决方案,这就是为什么有时需要自己动手的原因。 - Vinay Sajip
我指的是日志文件的名称,我需要从日志配置文件中获取它以检查它是否存在。在代码的这一点上获取该信息是不方便的。我同意您的观点,在启动时进行此类检查是理想的。也许我的真正问题是为什么日志模块不为用户处理这些类型的异常? - dicato
由于日志模块期望所给的日志文件名是可写的文件,并且存在于一个目录中。这种行为与最小惊奇原则一致。一个典型的文本编辑器(例如Windows上的记事本或Linux上的gedit),当在不存在的目录中使用文件名调用时,不会自动为您创建文件。记事本会给出错误消息;gedit不会将文件保存在该名称下,而是在单击“保存”时提示您选择位置。 - Vinay Sajip
2个回答

1
这不是特定于日志记录模块的问题:一般而言,Python 代码不会自动为您自动创建中间目录;您需要使用os.makedirs() 显式地创建中间目录,通常像这样:
if not os.path.exists(dirname):
    os.makedirs(dirname)

您可以使用子类替换日志记录中提供的标准FileHandler,该子类执行所需的检查,并在必要时使用os.makedirs()创建用于日志文件的目录。然后,您可以在配置文件中指定此处理程序,而不是标准处理程序。


那么为了正确处理日志模块中的异常,我需要对该模块进行子类化吗?虽然我认为这种设计是合理的,但似乎这是一种复杂的处理异常(或一组异常)的方式。 - dicato

0

假设这只需要在你的应用程序执行的开始时执行一次,我会先使用os.makedirs()创建所有需要的目录,而不检查它们是否已经存在,甚至不用等待日志模块抛出错误。如果在尝试启动记录器时出现错误,你可以按照你可能已经做过的方式来处理:打印一个错误消息,禁用记录器。你已经超出了只是尝试创建目录的范围。如果用户给你提供了虚假信息,那么你不会比现在更糟糕,在绝大多数情况下你反而会更好。


我对这种方法的担忧是,我需要解析日志配置文件,然后迭代检查args变量是否为文件处理程序,并采取行动。虽然这并不是什么难题,但它似乎是一个冗长的解决方案。为什么我需要这样做?为什么日志模块不能替我完成这个任务? - dicato
嗯,是的,一开始我没有注意到您传递给日志文件的文件名是日志配置文件而不是日志文件。看起来Vinay有一个使用自定义FileHandler的建议,这可能是更好的方法。 - kindall
虽然Vinay的解决方案肯定是一个好主意,但你觉得它对于这个问题来说是否有些过度了? - dicato
不,我认为这似乎是处理此类问题的预期方式。文件处理程序似乎是在您的示例代码中捕获异常的类,因此如果您可以替换自己的文件处理程序并根据需要创建目录,那么这对我来说似乎是相当精确的解决方法。 - kindall
手术式的,没错,但想象一下一个新手开发者只是试图使用日志记录模块。对于新手来说,这有点复杂。虽然,这可能是我走的路。 - dicato

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接