Python笔记 ·

Python日志记录:一个深入的教程

前言

本周的推荐来啦,一篇关于python的logging日志模块使用的文章。

原文Python Logging: An In-Depth Tutorial

作者:SON NGUYEN KIM

正文

随着应用程序变得越来越复杂,拥有良好的日志将会非常有用,不仅在调试时,而且为应用程序/性能问题提供数据分析的洞察力。

Python标准库附带一个 logging模块,它提供了大部分基本的记录功能。通过正确设置,日志消息可以提供有关日志何时何地被触发以及日志上下文(如正在运行的进程/线程)的大量有用信息。

尽管有这些优点,日志记录模块经常被忽略,因为它需要一些时间才能正确设置,并且在我看来,尽管完整,但官方日志记录文档位于https://docs.python.org/3/library/logging.html并没有真正给日志记录的最佳实践或突出一些日志记录的惊喜点。

这个Python日志教程并不意味着是日志模块上的完整文档,而是一个“入门指南”,它介绍了一些日志记录概念以及一些需要注意的“疑难杂症”。这篇文章将以最佳实践目的,并包含一些指向更高级日志记录主题的建议。

请注意,文章中的所有代码片段都假设您已经导入了日志记录模块:

Python日志的概念

本节概述了日志记录模块中经常遇到的一些概念。

Python日志级别

日志级别对应于给出日志的“重要性(importance)”:“error”日志应该比“warn”日志更紧急,而“debug”日志应该仅在调试应用程序时使用。

Python中有六个日志级别; 每个级别与指示日志严重性的整数相关联:NOTSET = 0,DEBUG = 10,INFO = 20,WARN = 30,ERROR = 40和CRITICAL = 50。

Python日志记录:一个深入的教程

除NOTSET之外,所有级别都非常简单(DEBUG <INFO <WARN),其特殊性将在下面讨论。

Python日志记录格式

日志格式化程序基本上通过向其添加上下文信息来丰富日志消息。知道何时发送日志,何处(Python文件,行号,方法等)以及诸如线程和进程之类的附加上下文(在调试多线程应用程序时可能非常有用)可能很有用。

例如,当通过日志格式化程序发送日志“hello world”时:

它会变成

Python日志记录:一个深入的教程

Python记录处理程序

日志处理程序是有效写入/显示日志的组件:在控制台console (通过StreamHandler),文件file (通过FileHandler)或通过SMTPHandler发送电子邮件等方式显示它。

每个日志处理程序有两个重要的字段

  • 一种将上下文信息添加到日志的格式化程序。
  • 日志级别,用于过滤掉级别较低的日志。所以具有INFO级别的日志处理程序不会处理DEBUG日志。

Python日志记录:一个深入的教程

标准库提供了一些处理程序,这些处理程序应该足够用于常见用例:https://docs.python.org/3/library/logging.handlers.html#module-logging.handlers。最常见的是StreamHandler和FileHandler:

Python记录器

记录器可能是代码中最经常使用的记录器,也是最复杂的记录器。新的记录器可以通过以下方式获得:

记录器有三个主要领域:

  • 传播(Propagate):决定是否应将日志传播到记录器的父级。默认情况下,其值为True。
  • 级别(A leve):与日志处理程序级别一样,日志级别用于过滤掉“不太重要”的日志。除了日志处理程序以外,只能在“子”记录程序中检查级别; 一旦日志传播给其父母,级别将不会被检查。这是一种不直观的行为。
  • 处理程序(Handlers):日志在到达记录器时将被发送到的处理程序列表。这允许灵活的日志处理 - 例如,您可以拥有一个文件日志处理程序,用于记录所有的DEBUG日志和仅用于CRITICAL日志的电子邮件日志处理程序。在这方面,记录器处理程序关系类似于发布者 - 消费者关系:一旦通过日志记录程度检查,日志将被广播给所有处理程序。

Python日志记录:一个深入的教程

记录器的名称是唯一的,这意味着如果创建了名称为“toto”的记录器,随后的调用logging.getLogger("toto")将返回相同的对象:

正如你可能猜到的,记录器有一个层次结构。在层次结构之上是根记录器,可以通过logging.root访问它。这个记录器在使用类似方法时被调用logging.debug()。默认情况下,根日志级别为WARN,因此每个具有较低级别的日志(例如通过logging.info("info"))都将被忽略。根记录器的另一个特殊之处在于,它会在首次记录级别大于WARN的日志时创建其默认处理程序。logging.debug()一般不建议直接或间接使用根记录器。

默认情况下,当创建一个新的记录器时,其父项将被设置为根记录器:

但是,记录器使用“点符号”,这意味着名为“ab”的记录器将成为记录器“a”的孩子。但是,只有在创建了记录器“a”的情况下,才会发生这种情况,否则“ ab“父母仍然是根。

当记录器根据级别检查决定日志是否应该通过时(例如,如果日志级别低于记录器级别,日志将被忽略),它使用其“有效级别”而不是实际级别。如果级别不是NOTSET,则有效级别与记录器级别相同,也就是说,从DEBUG到CRITICAL的所有值; 然而,如果记录器级别是NOTSET,则有效级别将是具有非NOTSET级别的第一个祖先级别。

默认情况下,新的记录器具有NOTSET级别,并且由于根记录器具有WARN级别,记录器的有效级别将为WARN。所以即使新的记录器附加了一些处理程序,这些处理程序也不会被调用,除非日志级别超过WARN:

默认情况下,记录器级别将用于决定日志传递:如果日志级别低于记录器级别,则日志将被忽略。

Python日志记录最佳实践

日志记录模块确实非常方便,但它包含一些怪癖,即使是最好的Python开发人员也可能导致长时间的头痛。以下是我认为使用此模块的最佳实践:

  • 配置根记录器,但从不在代码中使用它 - 例如,从不调用像这样的函数 logging.info(),实际上它会调用场景后面的根记录器。如果您想从您使用的库中捕获错误消息,请确保将根记录器配置为写入文件,例如,以使调试更容易。默认情况下,根记录器只输出到stderr,所以日志很容易丢失。
  • 要使用日志记录,请确保使用创建新的日志记录器logging.getLogger(logger name)。我通常 __name__用作记录器名称,但只要一致,任何东西都可以使用。要添加更多的处理程序,我通常会有一个返回记录器的方法(可以在https://gist.github.com/nguyenkims/e92df0f8bd49973f0c94bddf36ed7fd0中找到要点)。

之后可以创建新的记录器并使用它:

  • 使用RotatingFileHandler类(如本例中使用的TimedRotatingFileHandler而不是FileHandler),因为它会在文件达到大小限制时自动为您旋转文件,或者每天都执行该操作。
  • 使用Sentry,Airbrake,Raygun等工具自动为您捕捉错误日志。这在Web应用程序的上下文中特别有用,在该应用程序中,日志可能非常冗长,并且错误日志可能很容易丢失。使用这些工具的另一个优点是,您可以获取有关错误中变量值的详细信息,以便您知道哪些URL会触发错误,哪位用户担心等等。

如果您对更多最佳实践感兴趣,请阅读由Toptaler Martin Chikilian撰写的Python开发人员制作的10个最常见错误

理解基础知识

什么是调试工具?

调试工具是一种工具,它允许开发人员发现错误并调查问题。它可以是一个命令行工具,如gdb,pdb(用于Python)或可以嵌入IDE(Visual Studio, idea suites等)

关于作者

Son 对软件工程和ML算法非常熟练,并且总是尽力用简单而高效的方法解决问题,从而使代码长期可维护。作为一名企业家,他致力于他的工作,充分理解责任和主动性的重要性。他可以与商业和技术双方高效沟通。

参与评论