当你使用任何编程语言时,最终目标始终是拥有一个按照提供的指令正常运行的程序。然而,第一次就达到期望的结果是很少见的,你可能需要分析你的运行以识别错误。在现代软件中,理解运行时行为已成为标准做法,这就是日志记录派上用场的地方。
在本文中,我们将专注于Python的logging
库的基础知识,该库提供了所有必要的功能。它易于使用,在实施时提供了许多好处。虽然你可能目前正在使用print
函数来调试和验证结果,但在生产环境中,按照日志记录的标准进行工作是很重要的。Python的日志记录库可以让你做到这一点。
日志记录的级别
不同的日志级别是日志记录的一个重要方面,了解它们的工作原理至关重要。简而言之,你可以选择所需的级别来控制输出中出现的日志消息。logging
库为每个级别提供了单独的模块,它们的编号从10到50不等。所有级别都是不言而喻的,易于理解。但是,我们将详细介绍每个级别。
- Debug:此日志级别主要用于调试目的,在生产环境中没有用处。它的日志级别为10。
- Info:此日志级别提供了从程序行为中预期的信息。它的日志级别为20。
- Warning:此日志级别通知用户有意外事件发生,但不足以使程序停止。它的日志级别为30。
- Error:此日志级别用于报告运行时的意外故障。它会被提高以避免将来发生类似的故障。它的日志级别为40。
- Critical:此日志级别表示严重错误,可能导致应用程序停止运行。它的日志级别为50。
程序记录高于指定级别的消息,类似于级联。下面是它的工作原理示例。在basicConfig
中,我们应该提供所需的日志记录级别。结果,只有高于此级别的消息才会出现在输出中(包括当前级别)。因此,当我们想要查看WARNING
消息时,程序不会记录DEBUG
和INFO
级别的消息。
import logging
def test_logging():
logging.basicConfig(level=logging.WARNING)
logging.debug("DEBUG")
logging.info("INFO")
logging.warning("WARNING")
logging.error("ERROR")
logging.critical("CRITICAL")
test_logging()
# WARNING:root:WARNING
# ERROR:root:ERROR
# CRITICAL:root:CRITICAL
在开始时,我们将级别设置为WARNING
。因此,输出中只有三条消息 — 分别是WARNING
、ERROR
和CRITICAL
级别的消息。现在,让我们将级别修改为DEBUG
以观察输出。
import logging
def test_logging():
logging.basicConfig(level=logging.DEBUG)
logging.debug("DEBUG")
logging.info("INFO")
logging.warning("WARNING")
logging.error("ERROR")
logging.critical("CRITICAL")
test_logging()
# DEBUG:root:DEBUG
# INFO:root:INFO
# WARNING:root:WARNING
# ERROR:root:ERROR
# CRITICAL:root:CRITICAL
很好,现在你有一个高层次的理解,我们可以转向日志记录方法的更实际部分。
更改基本配置
在上一个示例中,我们只使用了标准配置,并收到了一个格式为INFO:root:
的输出,这并不是很信息丰富。然而,可以修改许多可选配置来获得所需的消息格式。
import logging
def test_logging():
logging.basicConfig(
level=logging.ERROR,
format="%(asctime)s | %(levelname)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
filename="basic.log")
logging.debug("DEBUG")
logging.info("INFO")
logging.warning("WARNING")
logging.error("ERROR")
logging.critical("CRITICAL")
test_logging()
# 2023-11-18 14:52:30 | ERROR | ERROR
# 2023-11-18 14:52:30 | CRITICAL | CRITICAL
日志的新格式更加信息丰富,因为它提供了日志的确切时间。这种格式不仅限于日期更改,并且可以使用filename
参数为basicConfig
定制格式以将日志存储在文件中。存储日志对于以后的分析在生产工作流程中非常有用。
在生产就绪的应用程序中,通常使用特定的存储系统,它允许弹性分析方法。此外,日志消息中可以使用变量,但是要谨慎使用,不要提供任何敏感信息,以避免日志可能在未来造成潜在泄漏。
import logging
def test_logging():
logging.basicConfig(
level=logging.INFO,
format="%(message)s")
for i in ["Dog", "Cat"]:
logging.info("%s is your animal!", i)
test_logging()
# Dog is your animal!
# Cat is your animal!
自定义日志格式
在前面的示例中,我们直接从库中调用了记录器。然而,也可以创建自定义对象并根据需要进行自定义。在某些情况下,这种技术可能很有用,因此理解如何实现它非常重要。
例如,在下面的示例中,我们创建了
一个名为Test
的记录器,并将其日志级别设置为DEBUG
。然而,仅仅设置日志级别不足以输出消息。我们必须使用处理程序来实现。Handle
本质上是一个将日志发送到特定目的地的对象。在这种情况下,我们将消息打印到控制台,但在生产环境中,你通常会将所有消息发送到一个特殊的存储或数据库。
import logging
logger = logging.getLogger('TEST')
logger.setLevel(logging.DEBUG)
handle = logging.StreamHandler()
handle.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handle.setFormatter(formatter)
logger.addHandler(handle)
logger.debug("DEBUG")
logger.info("INFO")
# 2023-11-18 15:47:41,035 - TEST - DEBUG - DEBUG
# 2023-11-18 15:47:41,035 - TEST - INFO - INFO
如你所见,我们添加了特定的格式,并在处理程序中使用了它。这个简单的示例表明,你可以创建多个记录器和处理程序实例,并根据需要将相关日志发送到每个目的地。此外,你还可以使用colorlog
库使用不同的颜色进一步修改你的格式。下面是用于记录级别的颜色实现示例。
import logging
import colorlog
logger = logging.getLogger('TEST')
logger.setLevel(logging.DEBUG)
handle = logging.StreamHandler()
handle.setLevel(logging.DEBUG)
fmt = colorlog.ColoredFormatter(
"%(name)s: %(white)s%(asctime)s%(reset)s %(log_color)s%(levelname)s%(reset)s %(process)d >>> %(log_color)s%(message)s%(reset)s"
)
handle.setFormatter(fmt)
logger.addHandler(handle)
logger.error("ERROR")
logger.info("INFO")
带有彩色格式的输出
这里是一个类似的方法的示例,用于创建数据库、将数据发送到数据库并在整个过程中记录一切。虽然下面的代码只是一个虚拟代码,但它仍然可以给你一个如何在实际情况下处理这样的任务的想法。
import logging
from time import sleep
def db_connection(user: str, password: str, logger: logging.Logger) -> str:
"""
虚拟数据库连接
"""
conn_str = user + password
logger.info("连接已启动")
sleep(1)
try:
conn = "连接成功"
logger.info("连接成功")
except:
logger.error("数据库连接期间出错")
return conn
def send_data(data: tuple, conn: str, logger: logging.Logger) -> None:
logger.info("数据已发送")
def main():
# 准备记录器
logger = logging.getLogger('TEST')
logger.setLevel(logging.DEBUG)
handle = logging.StreamHandler()
handle.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handle.setFormatter(formatter)
logger.addHandler(handle)
logger.info("程序已启动")
sleep(1)
conn = db_connection(user='test', password='test', logger=logger)
data = ("test", 123)
send_data(data=data, conn=conn, logger=logger)
main()
程序输出
结论
强烈建议将代码中的print
用法替换为logging
。日志记录提供了更多的灵活性,对于调试工作流程更加有用。此外,了解日志记录在生产中的工作原理以及如何阅读现有日志是至关重要的。建议尝试各种示例以彻底学习这个概念。花一点时间学习如何使用日志记录库,将对你未来的工作大有裨益。