介绍
logging
模块是标准Python库的一部分,并为软件运行时发生的事件提供跟踪。 您可以向代码添加日志记录,以指示发生了哪些事件。
logging
模块允许记录与应用程序操作相关的事件的诊断日志记录,以及记录用户事务事件以进行分析的审核记录。 它特别用于将事件记录到文件中。
为什么要使用logging
模块
logging
模块保存程序中发生的事件的记录,使得可以看到与一个软件的整个运行时间中发生的任何事件相关的输出。
您可能会更加熟悉在整个代码中使用print()
语句检查事件是否发生。 print()
语句提供了一个基本的方式来调试代码来解决问题。 尽管在整个代码中嵌入print()
语句可以跟踪执行流程和程序的当前状态,但由于以下几个原因,该解决方案证明比使用logging
模块更不易维护:
- 由于两者混合,因此难以区分调试输出和正常程序输出
- 当使用分散在代码中的
print()
语句时,没有简单的方法来禁用提供调试输出的那些 - 完成调试后,很难删除所有的
print()
语句 - 没有包含可用的诊断信息的日志记录
在代码中习惯使用logging
模块是一个好主意,因为这更适合于超越简单Python脚本的应用程序,并提供可持续的调试方法。
因为日志可以显示您的行为和错误随着时间的推移,它们还可以让您更好地了解应用程序开发过程中发生的情况。
将调试消息打印到控制台
如果您习惯使用print()
语句来查看程序中发生的情况,则可能习惯于查看定义类的程序,并实例化如下所示的对象:
class Pizza():
def __init__(self, name, price):
self.name = name
self.price = price
print("Pizza created: {} (${})".format(self.name, self.price))
def make(self, quantity=1):
print("Made {} {} pizza(s)".format(quantity, self.name))
def eat(self, quantity=1):
print("Ate {} pizza(s)".format(quantity, self.name))
pizza_01 = Pizza("artichoke", 15)
pizza_01.make()
pizza_01.eat()
pizza_02 = Pizza("margherita", 12)
pizza_02.make(2)
pizza_02.eat()
上面的代码有一个__init__
方法来定义Pizza
类的对象的name
和price
。 然后有两种方法,一种叫做make()
,用于制作比萨饼,另一种叫做eat()
,用于吃比萨饼。 这两种方法采用quantity
,初始化为1
。
现在我们来运行程序:
python pizza.py
我们将收到以下输出:
OutputPizza created: artichoke ($15)
Made 1 artichoke pizza(s)
Ate 1 pizza(s)
Pizza created: margherita ($12)
Made 2 margherita pizza(s)
Ate 1 pizza(s)
虽然print()
语句允许我们看到代码正在运行,但是我们可以使用logging
模块来代替。
我们在整个代码中删除或注释掉print()
语句,并将import logging
添加到文件的顶部:
import logging
class Pizza():
def __init__(self, name, value):
self.name = name
self.value = value
...
logging
模块的默认级别为WARNING
,高于DEBUG
。 由于我们将在本示例中使用logging
模块进行调试,因此我们需要修改配置,以便logging.DEBUG的级别将信息返回给我们的控制台。 我们可以通过在import语句下方添加以下行来做到这一点:
import logging
logging.basicConfig(level=logging.DEBUG)
class Pizza():
...
这个级别的logging.DEBUG
是指在上面的代码中引用的一个常量整数值来设置一个阈值。 DEBUG
级别为10。
现在,我们将用logging.debug()
语句替换所有的print()
语句。 与logging.DEBUG
不同, logging.debug()
是logging
模块的一种方法。 使用此方法时,可以使用传递给print()
的相同字符串,如下所示。
import logging
logging.basicConfig(level=logging.DEBUG)
class Pizza():
def __init__(self, name, price):
self.name = name
self.price = price
logging.debug("Pizza created: {} (${})".format(self.name, self.price))
def make(self, quantity=1):
logging.debug("Made {} {} pizza(s)".format(quantity, self.name))
def eat(self, quantity=1):
logging.debug("Ate {} pizza(s)".format(quantity, self.name))
pizza_01 = Pizza("artichoke", 15)
pizza_01.make()
pizza_01.eat()
pizza_02 = Pizza("margherita", 12)
pizza_02.make(2)
pizza_02.eat()
此时,当我们使用python pizza.py
命令运行程序时,我们将收到以下输出:
OutputDEBUG:root:Pizza created: artichoke ($15)
DEBUG:root:Made 1 artichoke pizza(s)
DEBUG:root:Ate 1 pizza(s)
DEBUG:root:Pizza created: margherita ($12)
DEBUG:root:Made 2 margherita pizza(s)
DEBUG:root:Ate 1 pizza(s)
日志消息具有严重性级别DEBUG
以及嵌入在其中的单词root
,它指的是Python模块的级别。 logging
模块可以与具有不同名称的记录器层次结合使用,以便您可以为每个模块使用不同的记录器。
例如,您可以将记录器设置为具有不同名称和不同输出的不同记录器:
logger1 = logging.getLogger("module_1")
logger2 = logging.getLogger("module_2")
logger1.debug("Module 1 debugger")
logger2.debug("Module 2 debugger")
OutputDEBUG:module_1:Module 1 debugger
DEBUG:module_2:Module 2 debugger
现在我们已经了解如何使用logging
模块将消息打印到控制台,我们继续使用logging
模块将消息打印到文件中。
将消息记录到文件
logging
模块的主要目的是将消息记录到文件而不是控制台。 保存邮件文件为您提供一段时间内的数据,您可以查询和量化数据,以便您可以看到需要对代码进行哪些更改。
要开始记录到文件,我们可以修改logging.basicConfig()
方法来包含一个filename
参数。 在这种情况下,我们来调用文件名test.log
:
import logging
logging.basicConfig(filename="test.log", level=logging.DEBUG)
class Pizza():
def __init__(self, name, price):
self.name = name
self.price = price
logging.debug("Pizza created: {} (${})".format(self.name, self.price))
def make(self, quantity=1):
logging.debug("Made {} {} pizza(s)".format(quantity, self.name))
def eat(self, quantity=1):
logging.debug("Ate {} pizza(s)".format(quantity, self.name))
pizza_01 = Pizza("artichoke", 15)
pizza_01.make()
pizza_01.eat()
pizza_02 = Pizza("margherita", 12)
pizza_02.make(2)
pizza_02.eat()
上面的代码与上一节中的代码相同,只是现在我们添加了要打印的日志的文件名。 一旦我们使用python pizza.py
命令运行代码,我们应该在我们的目录中有一个名为test.log
的新文件。
让我们用nano(或你所选择的文本编辑器)打开test.log
文件:
nano test.log
当文件打开时,我们将看到以下内容:
DEBUG:root:Pizza created: artichoke ($15)
DEBUG:root:Made 1 artichoke pizza(s)
DEBUG:root:Ate 1 pizza(s)
DEBUG:root:Pizza created: margherita ($12)
DEBUG:root:Made 2 margherita pizza(s)
DEBUG:root:Ate 1 pizza(s)
这与上一节中遇到的控制台输出类似,只不过它现在位于test.log
文件中。
我们用CTRL
+ x
关闭文件,然后移回pizza.py
文件,以便我们可以修改代码。
我们将保留大部分代码相同,但修改两个比萨实例中的pizza_01
和pizza_02
:
import logging
logging.basicConfig(filename="test.log", level=logging.DEBUG)
class Pizza():
def __init__(self, name, price):
self.name = name
self.price = price
logging.debug("Pizza created: {} (${})".format(self.name, self.price))
def make(self, quantity=1):
logging.debug("Made {} {} pizza(s)".format(quantity, self.name))
def eat(self, quantity=1):
logging.debug("Ate {} pizza(s)".format(quantity, self.name))
# Modify the parameters of the pizza_01 object
pizza_01 = Pizza("Sicilian", 18)
pizza_01.make(5)
pizza_01.eat(4)
# Modify the parameters of the pizza_02 object
pizza_02 = Pizza("quattro formaggi", 16)
pizza_02.make(2)
pizza_02.eat(2)
通过这些更改,我们再次使用python pizza.py
命令运行该程序。
一旦程序运行,我们可以再次使用nano打开我们的test.log
文件:
nano test.log
当我们查看该文件时,我们将看到添加了几行新行,并且上次运行该程序的行保留:
DEBUG:root:Pizza created: artichoke ($15)
DEBUG:root:Made 1 artichoke pizza(s)
DEBUG:root:Ate 1 pizza(s)
DEBUG:root:Pizza created: margherita ($12)
DEBUG:root:Made 2 margherita pizza(s)
DEBUG:root:Ate 1 pizza(s)
DEBUG:root:Pizza created: Sicilian ($18)
DEBUG:root:Made 5 Sicilian pizza(s)
DEBUG:root:Ate 4 pizza(s)
DEBUG:root:Pizza created: quattro formaggi ($16)
DEBUG:root:Made 2 quattro formaggi pizza(s)
DEBUG:root:Ate 2 pizza(s)
虽然这些信息肯定是有用的,但我们可以通过添加其他LogRecord属性来使日志更加翔实 。 首先,我们想添加一个可读时间戳,告诉我们LogRecord何时被创建。
我们可以将该属性添加到称为format
参数中,如表中所示,使用字符串%(asctime)s
进行引用。 另外,为了保持DEBUG
级别的名称,我们需要包含字符串%(levelname)s
,并保留我们要求记录器打印的字符串消息,我们将包含%(message)s
。 这些属性中的每一个将被colon
,如下面添加的代码所示。
import logging
logging.basicConfig(
filename="test.log",
level=logging.DEBUG,
format="%(asctime)s:%(levelname)s:%(message)s"
)
class Pizza():
def __init__(self, name, price):
self.name = name
self.price = price
logging.debug("Pizza created: {} (${})".format(self.name, self.price))
def make(self, quantity=1):
logging.debug("Made {} {} pizza(s)".format(quantity, self.name))
def eat(self, quantity=1):
logging.debug("Ate {} pizza(s)".format(quantity, self.name))
pizza_01 = Pizza("Sicilian", 18)
pizza_01.make(5)
pizza_01.eat(4)
pizza_02 = Pizza("quattro formaggi", 16)
pizza_02.make(2)
pizza_02.eat(2)
当我们使用python pizza.py
命令运行带有添加属性的代码时,我们将在test.log
文件中添加新行,其中包含可读时间戳以及DEBUG
的级别名称和相关联的作为字符串传递到记录器的消息。
OutputDEBUG:root:Pizza created: Sicilian ($18)
DEBUG:root:Made 5 Sicilian pizza(s)
DEBUG:root:Ate 4 pizza(s)
DEBUG:root:Pizza created: quattro formaggi ($16)
DEBUG:root:Made 2 quattro formaggi pizza(s)
DEBUG:root:Ate 2 pizza(s)
2017-05-01 16:28:54,593:DEBUG:Pizza created: Sicilian ($18)
2017-05-01 16:28:54,593:DEBUG:Made 5 Sicilian pizza(s)
2017-05-01 16:28:54,593:DEBUG:Ate 4 pizza(s)
2017-05-01 16:28:54,593:DEBUG:Pizza created: quattro formaggi ($16)
2017-05-01 16:28:54,593:DEBUG:Made 2 quattro formaggi pizza(s)
2017-05-01 16:28:54,593:DEBUG:Ate 2 pizza(s)
根据您的需要,您可能希望在代码中使用其他LogRecord属性 ,以使程序文件的日志与您相关。
将调试和其他消息记录到单独的文件中可以让您全面了解您的Python程序随着时间的推移,让您有机会以通过程序的历史工作通知的方式对代码进行故障排除和修改,以及发生的事件和事务。
记录级别表
作为开发人员,您可以通过添加严重性级别将重要级别归因于记录器中捕获的事件。 严重程度如下表所示。
记录级别在技术上是整数(一个常数),它们都以10的增量开始,从NOTSET
开始,其数值为0初始化记录器。
您还可以根据预定义的级别定义自己的级别。 如果您定义具有相同数值的级别,则将覆盖与该值相关联的名称。
下表显示了各种级别名称,它们的数值,可用于调用级别的功能以及该级别的用途。
水平 | 数值 | 功能 | 用于 |
---|---|---|---|
CRITICAL |
50 | logging.critical() |
显示严重错误,程序可能无法继续运行 |
ERROR |
40 | logging.error() |
显示更严重的问题 |
WARNING |
30 | logging.warning() |
表示意外发生或可能发生的事情 |
INFO |
20 | logging.info() |
确认事情按预期工作 |
DEBUG |
10 | logging.debug() |
诊断问题,显示详细信息 |
NOTSET
0初始化记录器
logging
模块将默认级别设置为WARNING
,因此默认情况下将logging
WARNING
, ERROR
和CRITICAL
。 在上面的示例中,我们修改了配置,将DEBUG
级别包含以下代码:
logging.basicConfig(level=logging.DEBUG)
您可以从官方logging
文档中阅读有关命令的更多信息和调试器。
结论
调试是任何软件开发项目的重要一步。 logging
模块是标准Python库的一部分,用于跟踪软件运行时发生的事件,并将这些事件输出到单独的日志文件,以便跟踪代码运行时发生的情况。 这为您提供了根据了解随时间运行程序而发生的各种事件来调试代码的机会。