当一个应用程序运行时,它会执行大量的任务。一个简单的待办事项应用程序可以有大量的任务,如:用户登录、创建待办事项、更新待办事项、删除待办事项和复制待办事项。这些任务可能导致成功,也可能以一些错误而告终。因此,有必要对发生的事件进行监控和分析,以确定应用程序的性能瓶颈。这就是日志记录的用处。
在这篇文章中,你将学习如何使用Python日志模块在Python应用程序中创建日志。日志可以帮助所有经验水平的Python开发者更快地开发和分析一个应用程序的性能。
什么是日志记录?
日志是存储应用程序中发生的事件的细节的过程。日志提供了一种额外的方式来检查一个应用程序正在成长的流程。它是应用程序状态的历史记录。
例如,如果一个应用程序崩溃了,如果没有任何关于崩溃前发生的细节,就很难追踪这个问题。在这种情况下,日志提供了一个日志跟踪,这可以帮助开发人员通过查看日志和在他们的机器上重新创建实际场景来检测问题的原因。通过正确设置的日志系统,你可以检测出错误的原因,精确到行号的水平。
开发人员何时以及为何要使用日志?
如果你是一个开发者,你可能已经使用print
语句来调试你的Python应用程序。当你想获得用于调试应用程序的信息时,你需要的信息比你想象的要多得多,比如时间戳、相关模块和类型。你可以在一个小的应用程序中使用print
命令,但是在一个大型的、复杂的应用程序中,这种策略很快就变得不方便了,因为在这个应用程序中,多个模块在互相通信和共享数据。
为了解决这个问题,你需要一个完善的日志模块,它能够以结构化和可预测的方式将与你的应用程序相关的各种信息写到你的一个输出流中(例如,控制台或日志文件)。
日志的级别
在Python日志模块中,有五个与事件的严重性有关的标准级别:
- Debug(10)。主要用于非生产环境,诊断问题或获得特定类型的信息。
- Info(20)。用于输出事件信息并创建一个执行跟踪。
- 警告(30)。当一些意外的事情发生,并可能在将来导致问题时使用。
- Error(40)。当一个问题发生,破坏了应用程序的正常功能时使用。
- Critical(50)。当应用程序的一个或多个部分不能运行时使用。
根据事件的严重程度,你可以创建一个日志,甚至可以将日志器配置为只记录低于或高于特定严重程度的事件。
在本文后面的章节中,你将学习如何在日志模块中使用这些严重性级别。
在Python中使用日志模块
为了理解Python中的日志概念,通过在终端运行以下命令创建一个项目目录(python-logging
)。
mdkir python-logging
cd python-logging
在python-logging
目录中,创建一个main.py
文件,并在其中添加以下代码。
# 1
import logging
# 2
logging.debug('This is a debug message')
logging.info('This is a info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')
在上面的代码中,你正在进行以下工作:
- 你正在导入 Python 内置的
logging
模块。 - 你正在使用
logging
模块的辅助函数,在控制台窗口上打印与五个严重程度相关的日志。
通过在终端运行以下命令来运行main.py
文件:
python main.py
如果你看到终端窗口中的输出,你会发现只打印了与警告、错误和关键严重程度相关的日志。
这是因为,在默认情况下,日志模块只打印严重程度等于或大于警告的日志。
配置严重性级别
要覆盖默认的严重性级别,你可以通过使用basicConfig
函数并向其传递level
参数来配置日志模块。要做到这一点,请更新你现有的main.py
文件,包括以下内容。
import logging
logging.basicConfig(level=logging.DEBUG) # added
logging.debug('This is a debug message')
logging.info('This is a info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')
在上面的代码中,你正在进行以下工作:
- 你正在使用
basicConfig
函数来配置logging
模块的设置。你将level
参数指定为logging.DEBUG
,这将配置logging
模块来记录所有严重程度等于或大于debug级别的消息。
通过在终端运行以下命令来运行main.py
文件。
python main.py
这一次,你会看到所有的日志都打印在你的终端窗口。
由日志模块创建的所有严重性级别的日志。
根据你的要求,你可以配置你的应用程序的严重性级别。例如,在开发应用程序时,你可以把level
参数设置为logging.DEBUG
,而在生产环境中,你可以把它设置为logging.INFO
。
在Python中把日志写到日志文件中
当你在开发一个应用程序时,把日志写到控制台是很方便的。然而,在非开发环境中,你可能需要一种方法来持久性地存储应用程序的日志。为此,你可以将你的日志存储在一个日志文件中。这个日志文件可以在任何时候被访问,用来调试问题或提取与你的Python应用程序中发生的事件有关的各种信息。
basicConfig
为了创建一个日志文件,你可以在filename
和filemode
函数中使用参数。
用下面的代码替换main.py
文件的内容。
import logging
# 1
logging.basicConfig(filename='app.log', filemode='w', level=logging.DEBUG)
logging.warning('This is a warning message')
logging.debug('This is a debug message')
在上面的代码中,你正在进行以下工作:
- 你正在指定
basicConfig
函数中的filename
和filemode
参数。这种配置在你的Python应用程序的根部创建了一个app.log
文件,并将所有的日志存储在其中,而不是将它们打印到控制台窗口。filemode
参数应该处于写(w
) 或追加(a
) 模式,以允许Python创建和写入日志文件。这里需要注意的是,写模式将在每次应用程序运行时重新创建日志文件,而append模式将把日志添加到日志文件的末尾。附加模式是默认模式。
通过在终端运行以下命令来运行main.py
文件。
python main.py
接下来,在你的应用程序的根部检查app.log
文件。你会看到下面的日志信息。
日志被存储在一个日志文件中。
自定义日志信息格式
通过使用basicConfig
函数中的format
参数,你可以记录在你的 Python 应用程序中发生的事件的更多信息。format
参数允许你定制你的日志消息格式,如果需要的话,在大量属性列表的帮助下,包括更多的信息。例如,你可以在你的日志消息中包括一个时间戳。
要做到这一点,请更新main.py
文件,用以下代码替换其内容:
import logging
# 1
logging.basicConfig(format='%(asctime)s - %(levelname)s - %(name)s - %(message)s')
logging.info('This is a custom format log')
在上述代码中,请记住以下几点:
format
参数需要一个字符串,你可以在其中以任何你喜欢的安排使用 LogRecord 属性。%(asctime)s
属性将当前的时间戳添加到日志信息中。
通过在终端执行以下命令来运行上述代码。
python main.py
在终端窗口,你会看到带有时间戳信息和自定义格式的日志消息:
一个详细的例子
现在你对Python中的日志工作有了一个概念,让我们把这些例子扩展到一个实际的Python程序中,看看日志是如何发挥作用的。在这个例子中,你将看到如何在try-except
块中使用logging.error
函数。
在main.py
文件中,用下面的内容替换现有的代码:
import logging
logging.basicConfig(
format='%(asctime)s - %(levelname)s - %(name)s - %(message)s')
# 1
a = 10
b = 'hello world'
# 2
try:
c = a / b
# 3
except Exception as e:
logging.error(e)
在上面的代码中,你正在进行以下工作:
- 你正在声明两个变量,一个int
a
和一个字符串b
。 - 在
try-expect
语句的try
块中,你正在对一个int(a
) 和一个字符串(b
) 进行除法操作。你可能已经知道,这不是一个有效的操作,会引起一个异常。 - 在
expect
块中,你正在捕捉这个异常(e
),并使用logging.error
函数记录它。
通过在终端执行以下命令来运行上述代码:
python main.py
在终端窗口,你会看到一个自定义格式的日志信息,说明不支持的操作错误:
用于记录的第三方Python模块
除了Python内置的日志库,还有一些第三方日志库,你可以在Python应用程序中使用。
Django中的日志记录
Django是最流行的基于Python的Web应用开发框架。LOGGING
在内部,Django使用Python日志模块,但允许开发者通过配置settings.py
文件中的配置变量来定制日志设置。
例如,下面的配置将所有日志输出写入应用程序根部的debug.log
文件。
LOGGING = {
'version': 1,
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': 'debug.log'
}
},
'loggers': {
'django': {
'handlers': [
'file'
],
'level': 'DEBUG'
}
}
}
这个设置更新了由Django根模块、Django服务器、Django Web请求和Django数据库查询产生的日志。更多关于配置Django日志的信息,请查看Django的文档。
Flask中的日志记录
Flask和Django一样,也是一个基于Python的Web应用开发框架,但它的规模比Django小。它也使用Python的内置日志模块,并提供了一个app.logger
函数来配置日志设置。
例如,为了将日志以JSON格式发送到控制台,你可以在初始化app
,在Flask应用中使用以下配置:
from logging.config import dictConfig
dictConfig({
'version': 1,
'formatters': {
'json': {
'()': 'pythonjsonlogger.jsonlogger.JsonFormatter'
}
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'json'
},
},
'root': {
'level': 'INFO',
'handlers': ['console']
}
})
app = Flask(__name__)
以JSON格式存储日志
与其以纯文本格式记录事件,你可能想把它们输出为JSON对象。通过JSON格式,你可以很容易地将记录添加到数据库中,或使用基于JSON的查询语言进行查询。对于这个用例,你可以利用python-json-logger库来存储JSON格式的日志。
要在你的Python应用程序中安装这个包,在终端运行以下命令:
pip install python-json-logger
开始使用这个库的基本代码如下:
import logging
import json_log_formatter
formatter = json_log_formatter.JSONFormatter()
json_handler = logging.FileHandler(filename='log.json')
json_handler.setFormatter(formatter)
logger = logging.getLogger('my_json')
logger.addHandler(json_handler)
logger.error('An error occurred', extra={'type':'fatal'})
这个包可以创建JSON日志,如下图所示:
结论
监控应用程序中发生的事件是非常重要的,最推荐的方法之一是使用日志。如果不跟踪你的应用程序中发生的事情以及用户是如何使用它的,你将永远无法识别性能瓶颈。有一句话说,你只能改善你能测量的东西。如果你不能改善你的系统,随着时间的推移,你会失去客户。因此,建议在你的应用程序中使用日志记录。