日志记录与print()的优点+最佳实践

7
我目前正在开发 pyftpdlib 模块的 1.0.0 版本。 这个新版本将引入一些不兼容的更改,因为某些 API 将不再接受字节,而是接受 Unicode 字符串。 顺便说一下,在这个变革的过程中,我在考虑是否要摒弃当前使用 print 语句的记录函数,并改用 logging 模块。 截至目前,pyftpdlib 将日志委托给了 3 个函数:
def log(s):
   """Log messages intended for the end user."""
   print s

def logline(s):
   """Log commands and responses passing through the command channel."""
   print s

def logerror(s):
   """Log traceback outputs occurring in case of errors."""
   print >> sys.stderr, s

想要自定义日志(例如将其写入文件)的用户只需按以下方式覆盖这3个函数:

>>> from pyftpdlib import ftpserver
>>>
>>> def log2file(s):
...        open('ftpd.log', 'a').write(s)
...
>>> ftpserver.log = ftpserver.logline = ftpserver.logerror = log2file

现在我想知道:放弃这种方法并使用日志模块会有什么好处? 从模块供应商的角度来看,我应该如何在我的模块中公开日志功能? 我应该这样做吗:

import logging
logger = logging.getLogger("pyftpdlib")

我在文档中说明“logger”是用户想要自定义日志行为时应使用的对象。那么故意设置预定义格式输出是否合法?

FORMAT = '[%(asctime)] %(message)s'
logging.basicConfig(format=FORMAT)
logger = logging.getLogger('pyftpdlib')

你能想到一个第三方模块,我可以借鉴其中将日志功能公开并作为公共API的部分吗?

先行致谢。

4个回答

4

库(FTP服务器或客户端库)不应该初始化日志系统。因此,可以实例化一个记录器对象,并在文档中指向logging.basicConfig(或提供类似于basicConfig的函数,具有更高级的输出,并让用户在其日志配置策略、普通basicConfig或库提供的配置之间进行选择)。

框架(如Django)或服务器(FTP服务器守护程序)应该初始化日志系统为合理的默认值,并允许自定义日志系统配置。


2
通常,库应该只创建一个NullHandler处理器,它仅是一个不执行任何操作的处理器。使用您的库的最终用户或应用程序开发人员可以配置日志系统。有关更多信息,请参见logging文档中的为库配置日志记录部分。特别是,请查看以下注释:

强烈建议您不要在库的记录器中添加除NullHandler之外的任何处理器。

在您的情况下,按照日志记录文档的说明,只需创建一个日志处理器即可。

import logging
logging.getLogger('pyftpdlib').addHandler(logging.NullHandler())

编辑:在问题中勾画的日志记录实现似乎完全合理。在您的文档中,只需提到logger并讨论或指向logging.setLevellogging.setFormatter方法以自定义库输出。与其使用logging.basicConfig(format=FORMAT),您可以考虑使用logging.config.fileConfig来管理输出设置,并在文档中的某个位置记录配置文件,再次将用户指向日志记录模块文档以了解此文件中预期的格式。


这是否意味着默认情况下不会产生任何输出? 如果是这样,我对此感到不满意。 - Giampaolo Rodolà
默认情况下,这不会产生任何输出。此外,logging.NullHandler 仅在 Python 2.7(和 > 3.2,我想)中可用,这可能是一个问题。如果您只使用 logger = logging.getLogger('pyftpdlib'),则将默认获取消息,并且您可以将用户指向 logging.setLevellogging.setFormatter 文档以演示如何配置库的输出消息。另外,您可以使用文件配置 logging.config.fileConfig 来允许用户配置记录器。 - Chris
此外,为了回答你最初的问题,使用日志记录而不是打印语句的好处在PEP 282中简要提到:如果单个日志记录机制被确立在标准库中,1)更有可能做好日志记录,2)多个库将能够集成到较大的应用程序中,这些应用程序可以被合理地记录。 - Chris
1
关于我关注的问题的最新更新是默认情况下没有输出。这是基于这样的假设,即用户应该在自己的代码中调用logging.basicConfig(),如果他/她忘记这样做,就不会提供日志记录。我通过在模块的“start()”方法中添加这个“if not logging.getLogger().handlers: logging.basicConfig()”来解决这个问题。这样,即使用户没有配置日志记录,模块也会决定进行日志记录以产生日志。 - Giampaolo Rodolà

0

这是我用来制作可定制的记录器的资源。我没有改变太多,只是添加了一个if语句,并传入是否要将日志记录到文件还是仅记录到控制台。

看看这个Colorer。它非常适合为输出着色,使DEBUG看起来不同于WARN,而INFO则不同于它们。

Logging模块捆绑了大量很好的功能,如SMTP日志记录、文件轮换日志记录(这样你可以保存几个旧的日志文件,但不会在每次出现问题时创建数百个文件)。

如果您想迁移到Python 3,使用logging模块将消除更改打印语句的需要。

根据您的需求,Logging非常棒。我以前只是轻微地使用它来查看程序运行到哪里(如果您正在运行此函数,则以这种方式着色),但它比普通的打印语句具有显着更强的功能。


0

你可以查看Django(只需创建一个示例项目),并查看它如何初始化日志记录子系统。

我之前写过一个上下文日志记录器助手 - 这个日志记录器会自动获取它所初始化的模块/类/函数的名称。这对于调试消息非常有用,因为你可以清楚地看到哪个模块输出了消息以及调用流程如何进行。


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