在Python中,为什么要使用日志记录(logging)而不是print输出?

182
在复杂项目中进行简单调试时,是否有理由使用Python日志记录器而不是print?其他用例呢?每个用例的最佳实践是否被接受(特别是当您只查找标准输出时)?
我一直听说这是一种“最佳实践”,但我一直没有弄清楚为什么。

17
对于大型项目,记录日志始终是一种“最佳实践”,因为您可以轻松地打开或关闭它,并获得更多或更少的信息。print则没有这些优点。 - Chris Eberle
1
请参见 http://blog.tplus1.com/index.php/2007/09/28/the-python-logging-module-is-much-better-than-print-statements/。 - agf
3
我认为print从来没有最好的使用场景。 - SingleNegationElimination
8
Python的日志记录文档称,print最佳使用场景是在命令行应用程序中显示用户帮助信息。 - slushy
1
@information_interchange 是的,当你编写永远不会失败、不会抛出任何异常和不会崩溃的代码时,你不需要任何日志记录;你不需要了解任何错误或故障,因为它们从一开始就是不可接受的。 - ivan866
显示剩余5条评论
7个回答

198

日志记录包具有许多有用的功能:

  • 很容易看到从哪里和何时(甚至是哪一行)进行了日志记录调用。
  • 您可以同时将日志记录到文件、套接字或几乎任何其他媒介。
  • 您可以根据严重程度区分日志记录。

打印功能没有这些功能。

此外,如果您的项目旨在被其他Python工具导入,那么将内容打印到标准输出(stdout)是一个不好的做法,因为用户很可能不知道打印消息来自哪里。使用日志记录,您的包的用户可以选择是否要传播来自您的工具的日志消息。


2
说得很好。有时我会在调试一个只打算运行一次的临时脚本时使用print,但任何将被其他人看到或需要持续超过一天的代码都会使用logger。 - TimothyAWiseman
  1. 我知道打印语句的确切位置,因为我通常会将其写在我想要调试的特定位置。
  2. 记录到文件和套接字?如果我只是在编写代码时进行调试,我不明白为什么我要将日志记录到这些地方。
  3. 你可以根据严重程度进行区分。嗯,当我使用打印语句时,通常是因为我对一个特定的错误感兴趣。 总之,对于调试我的代码来说,打印语句已经足够了。我会偶尔使用调试器,但在编码时使用日志记录似乎是不必要的。
- undefined

36

正确记录日志的最大优势之一是,您可以将消息分类并根据需要打开或关闭它们。例如,在项目的某个特定部分打开调试级别的消息可能很有用,但对于其他部分则可能会降低其重要性,因此不会被信息过载所占领,并且更容易集中精力完成需要记录日志的任务。

此外,日志是可配置的。您可以轻松地对其进行过滤、发送到文件、格式化、添加时间戳以及任何其他全局需求。而使用print语句就无法方便地进行管理。


4
肯定支持将输出发送到文件。在事后解析日志文件比必须在打开的控制台窗口中再次使其崩溃以查找错误要好得多。基本上,记录器非常适合在脚本失败后需要调试它而不是在它正在失败时进行调试的任何时间。它还非常适合在需要分析程序输出的复杂问题时进行调试的任何时间。基本上,每当您处理比语法错误更复杂的错误时,记录器都可能会为您简化这些操作。 - Jonathanb

20

打印语句有点像将在线调试器的负面方面与诊断工具结合在一起,既需要修改程序,但并不能从中获得更多有用的代码。

在线调试器允许您检查运行中程序的状态;但真正调试器的好处在于您无需修改源代码,在调试会话之前或之后都不需要;您只需将程序加载到调试器中,告诉调试器您要查看哪些内容,然后就可以开始了。

应用程序诊断可能需要一些前期工作,以某种方式修改源代码,但生成的诊断输出可以具有巨大的详细信息,并且可以按非常特定的程度开启或关闭。Python日志记录模块不仅可以显示已记录的消息,还可以显示调用它的文件和函数、如果有的话则为回溯,实际发出消息的时间等等。此外,诊断工具永远不需要被移除;当程序完成并投入生产时,它与添加的那天一样有效和有用;但它的输出可以被粘贴到日志文件中,这不太可能会干扰任何人,或者日志级别可以降低以使除最紧急的消息外所有消息都不会被记录。

预见调试的需要或使用实际上并不比在测试时使用ipython并熟悉其用于控制内置pdb调试器的命令更难。

当您发现自己认为使用打印语句比使用pdb更容易时(通常情况下),您会发现使用日志记录器可以使您的程序处于更易于处理状态,而不是使用并随后删除打印语句。

我将我的编辑器配置为将打印语句突出显示为语法错误,将日志记录声明突出显示为注释,因为我认为它们就像注释一样。


大部分的调试都是因为我自己的经验不足。一旦我意识到自己做错了什么,我就想删除打印语句,而不是留下令人尴尬的证明,说明我需要进行多少调试。对于这些情况,您还建议使用日志记录吗? - undefined

6
简而言之,使用日志记录库的优点胜过使用print,原因如下:
  • 控制输出内容
  • 定义要包含在日志中的信息类型
  • 配置输出日志时的样式
  • 最重要的是,设置日志的目标位置

详细来说,按严重程度级别进行分段日志事件是一种筛选出在特定时间内可能最相关的日志消息的好方法。日志事件的严重程度级别也给出在看到特定消息时应该有多担心的指示。例如,将日志类型分成调试(debug)信息(info)警告(warning)关键(critical)错误(error)等级。当您试图了解应用程序出了什么问题时,时间可能是非常重要的。您想知道诸如:
  • “这个问题是在我的数据库连接死亡之前还是之后发生的?”
  • “那个请求确切地什么时候进来的?”
此外,可以通过行号和文件名或方法名称甚至线程轻松查看日志发生的位置。
这里有一个名为loguru的Python功能性日志记录库。

5

如果您使用日志记录,那么负责部署的人员可以配置记录器将其发送到自定义位置,并提供自定义信息。如果只是打印,则只能获得这些内容。


1
Logging本质上创建了一个可搜索的纯文本数据库,其中包含其他元数据(时间戳、日志级别、行号、进程等)的打印输出。
这是纯金,我可以在Python脚本运行对日志文件运行egrep。 我可以调整我的egrep模式搜索以精确地选择我感兴趣的内容并忽略其余部分。这种减少认知负荷和通过试错自由选择我的egrep模式的好处对我来说至关重要。
tail -f mylogfile.log | egrep "key_word1|key_word2"

现在加入其他很酷的功能,如发送到套接字、设置调试级别、日志轮换、添加元数据等等,你有充分的理由选择记录日志而不是普通的打印语句。我倾向于使用打印语句,因为它很懒惰、很容易,添加记录需要一些样板代码,嘿,我们有 yasnippets(emacs)和 ultisnips(vim)以及其他模板工具,那么为什么要放弃记录日志而使用普通的打印语句呢?

0

除了提到的其他优点外,我想补充一下,在标准配置中,打印函数是有缓冲区的。刷新只会在当前块(包含打印命令的块)的末尾发生。

对于任何在非交互式 shell 中启动的程序(例如 codebuild、gitlab-ci)或其输出被重定向的程序而言,情况都是如此。

如果由于某种原因程序被终止(kill -9、计算机硬重置等),那么如果你使用 print 命令,你可能会错过一些日志信息。

然而,日志库将确保立即刷新打印到 stderr 和 stdout 的日志信息。


你对“块(block)”的定义是什么? - Sören
甚至更晚。函数调用的持续时间与缓冲区存储时间无关。 - Sören
无论如何,只有当demo返回时,打印才会出现,并且在给定的示例中,我们可以合理地预期它将在大约120秒左右出现。 - Floh
可以使用 print("hello world", flush=True) - Jason
flush参数是从Python 3.3开始的有效解决方案。 然而,内置的日志记录模块是日志记录用例的最佳解决方案。 - Floh
显示剩余3条评论

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