在Python中使用日志包是你的API的一部分

125 阅读2分钟

我们有一个记录电子邮件附件类型信息的Python程序。 作为这项工作的一部分,它想窥视各种档案类型的内部,看看里面有什么,因为恶意软件会把坏东西放在里面。我们为此使用的Python模块之一是Ubuntu打包的libarchive-c版本,它是libarchive的一个Python API。我们的程序以一种非常具体的输出格式打印出信息,然后我们的Exim配置读取并利用这些信息。

最近,我在看我们的电子邮件日志时注意到,它有一个非常不寻常的状态报告。正常的状态报告是这样的:

1kX88D-0004Mb-PR attachment application/zip; MIME file ext: .zip; zip exts: .iso

这个邮件的状态报告是:

Pathname cannot be converted from UTF-16BE to current locale.

这不是我们的程序发出的信息。而是来自C语言libarchive库的一个警告信息。然而,它并不是由C代码直接打印出来的;相反,这个报告是作为附加在库调用结果上的额外警告传递上去的。是libarchive-c决定把它打印出来,在一个通用的 FFI 支持函数中。 更具体地说,libarchive-c 决定通过 Python的 logging 包来 "记录 "它;默认的日志环境会将它打印到标准错误上。

(我们的程序并没有使用 logging,而且我不知道它在使用,直到我试图追踪这个问题)。

一个程序的输出在实践中往往是其API的一部分。当代码做了一些在默认情况下产生输出的事情时,这就改变了它所在的程序的API。如果警告信息应该被暴露出来,那么它应该通过实际的API(可访问的API)浮出水面,而不是随意抛出。 如果你的代码确实使用了 logging,这应该是它的API文档的一部分,而不是作为一个实现细节被塞在一个角落里,因为人们有理由想知道这个(所以他们可以配置 logging所以他们可能想把它关掉。

在一个相关的问题中,注意到libarchive-cimport 时构建了它要使用的日志器(这里),在你的 Python 代码通常有机会配置日志之前,甚至在某些情况下会在导入时使用它(这里这里),因为它正在动态地构建一些绑定关系。我猜想,就在导入时构建甚至使用其日志器而言,它远不是唯一的。

(自然是在程序启动时,在main() 函数或从它下来的东西中配置日志,而不是在程序加载时才开始做imports。这尤其是由于你在程序中如何做日志可能取决于命令行参数或其他配置信息。)